Skip to content

chore: update bid status to show 'live auction' in grey after live auction opens or hide bids info #12583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions src/app/Scenes/Inbox/Components/ActiveBids/ActiveBid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Image } from "@artsy/palette-mobile"
import { themeGet } from "@styled-system/theme-get"
import { ActiveBid_bid$data } from "__generated__/ActiveBid_bid.graphql"
import { BodyText, MetadataText } from "app/Scenes/Inbox/Components/Typography"
// eslint-disable-next-line no-restricted-imports
import { navigate } from "app/system/navigation/navigate"
import React from "react"
import { TouchableWithoutFeedback } from "react-native"
Expand Down Expand Up @@ -49,13 +50,15 @@ const StatusLabel = styled(MetadataText)`
return themeGet("colors.copper100")
case "losing":
return themeGet("colors.red100")
case "live_auction":
case "live_active":
return themeGet("colors.blue100")
case "live_auction":
return themeGet("colors.mono60")
}
}};
`

type BidStatus = "winning" | "reserve" | "losing" | "live_auction"
type BidStatus = "winning" | "reserve" | "losing" | "live_active" | "live_auction"

interface Props {
bid: ActiveBid_bid$data
Expand All @@ -76,10 +79,13 @@ class ActiveBid extends React.Component<Props, State> {

updateStatus() {
const bid = this.props.bid
const isInLiveOpenAuction = bid.sale && bid.sale.is_live_open
const isInLiveOpenAuction = bid?.sale?.isLiveOpen
const isInLiveAuction = bid?.sale?.isLiveOpenHappened

let status: BidStatus = "losing"
if (isInLiveOpenAuction) {
status = "live_active"
} else if (isInLiveAuction) {
status = "live_auction"
} else {
const leadingBidder = bid.is_leading_bidder
Expand All @@ -95,8 +101,10 @@ class ActiveBid extends React.Component<Props, State> {

get statusLabel(): string {
switch (this.state.status) {
case "live_auction":
case "live_active":
return "Join Live"
case "live_auction":
return "Live auction"
case "winning":
case "reserve":
return "Highest Bid"
Expand All @@ -109,7 +117,7 @@ class ActiveBid extends React.Component<Props, State> {
const bid = this.props.bid
// push user into live auction if it's open; otherwise go to artwork
const href =
this.state.status === "live_auction"
this.state.status === "live_active" || this.state.status === "live_auction"
? bid?.sale?.href
: bid?.most_recent_bid?.sale_artwork?.artwork?.href

Expand All @@ -130,15 +138,18 @@ class ActiveBid extends React.Component<Props, State> {

const headline = `Lot ${lotNumber} · ${artistName} `

const isInOpenLiveAuction = this.props.bid.sale && this.props.bid.sale.is_live_open
const isInOpenLiveAuction = this.props.bid.sale && this.props.bid.sale.isLiveOpen
const isInLiveAuction = this.props.bid.sale && this.props.bid.sale.isLiveOpenHappened
const bidderPositions = bid?.sale_artwork?.counts?.bidder_positions || null
const bidderPositionsLabel = bidderPositions + " " + (bidderPositions === 1 ? "Bid" : "Bids")

const subtitle = isInOpenLiveAuction
? "Live bidding now open"
: `${bid?.sale_artwork?.highest_bid?.display || ""} (${
bidderPositions ? bidderPositionsLabel : ""
})`
: isInLiveAuction
? "Live auction"
: `${bid?.sale_artwork?.highest_bid?.display || ""} (${
bidderPositions ? bidderPositionsLabel : ""
})`

return (
<TouchableWithoutFeedback accessibilityRole="button" onPress={this.handleTap}>
Expand All @@ -164,7 +175,8 @@ export default createFragmentContainer(ActiveBid, {
is_leading_bidder: isLeadingBidder
sale {
href
is_live_open: isLiveOpen
isLiveOpen
isLiveOpenHappened
}
most_recent_bid: mostRecentBid {
id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ it("renders without throwing a error", () => {
})

it("looks right for bids in live open auctions", () => {
const tree = renderWithWrappersLEGACY(<ActiveBid bid={bid(true)} />)
expect(extractText(tree.root)).toMatch("Live bidding now open")
const view = renderWithWrappersLEGACY(<ActiveBid bid={bid(true, true)} />)
expect(extractText(view.root)).toMatch("Live bidding now open")
})

const bid = (isOpen?: boolean) => {
it("looks right for bids in live closed auctions", () => {
const view = renderWithWrappersLEGACY(<ActiveBid bid={bid(false, true)} />)
expect(extractText(view.root)).toMatch("Live auction")
})

const bid = (isLiveOpen?: boolean, isLiveOpenHappened?: boolean) => {
return {
is_leading_bidder: false,
sale: {
is_live_open: isOpen,
isLiveOpen: isLiveOpen,
isLiveOpenHappened: isLiveOpenHappened,
href: "/to-the-auction",
},
most_recent_bid: {
Expand Down
6 changes: 4 additions & 2 deletions src/app/Scenes/MyBids/Components/ActiveLotStanding.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ActionType, ContextModule, OwnerType } from "@artsy/cohesion"
import { Flex, Text } from "@artsy/palette-mobile"
import { ActiveLotStanding_saleArtwork$data } from "__generated__/ActiveLotStanding_saleArtwork.graphql"
// eslint-disable-next-line no-restricted-imports
import { navigate } from "app/system/navigation/navigate"
import { useScreenDimensions } from "app/utils/hooks"
import { TouchableOpacity } from "react-native"
Expand Down Expand Up @@ -45,6 +46,7 @@ export const ActiveLotStanding = ({
destination_screen_owner_id: saleArtwork?.artwork?.internalID,
destination_screen_owner_slug: saleArtwork?.artwork?.slug,
})
// eslint-disable-next-line no-restricted-imports
navigate(saleArtwork?.artwork?.href as string)
}

Expand All @@ -56,7 +58,7 @@ export const ActiveLotStanding = ({
>
<Flex flexDirection="row" justifyContent="space-between">
<Lot saleArtwork={saleArtwork} isSmallScreen={isSmallScreen} />
{!sale.isLiveOpen && (
{!sale.isLiveOpenHappened && (
<Flex>
<Flex flexDirection="row" alignItems="center" justifyContent="flex-end">
<Text variant="xs">{sellingPrice}</Text>
Expand Down Expand Up @@ -90,7 +92,7 @@ export const ActiveLotStandingFragmentContainer = createFragmentContainer(Active
isHighestBidder
sale {
status
isLiveOpen
isLiveOpenHappened
liveStartAt
endAt
}
Expand Down
15 changes: 9 additions & 6 deletions src/app/Scenes/MyBids/Components/BiddingStatuses.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,15 @@ export const Passed = () => (
)

export const BiddingLiveNow = () => (
<>
<Text variant="xs" color="blue100">
{" "}
Bidding live now
</Text>
</>
<Text variant="xs" color="blue100">
Bidding live now
</Text>
)

export const LiveAuction = () => (
<Text variant="xs" color="mono60">
Live auction
</Text>
)

export const Watching = () => (
Expand Down
5 changes: 3 additions & 2 deletions src/app/Scenes/MyBids/Components/ClosedLotStanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { StarCircleFillIcon } from "@artsy/icons/native"
import { Flex, Text } from "@artsy/palette-mobile"
import { ClosedLotStanding_saleArtwork$data } from "__generated__/ClosedLotStanding_saleArtwork.graphql"
import { TimelySale } from "app/Scenes/MyBids/helpers/timely"
// eslint-disable-next-line no-restricted-imports
import { navigate } from "app/system/navigation/navigate"
import moment from "moment-timezone"
import { TouchableOpacity } from "react-native"
Expand Down Expand Up @@ -84,7 +85,7 @@ export const ClosedLotStanding = ({
>
<Flex flexDirection="row" justifyContent="space-between">
<Lot saleArtwork={saleArtwork} subtitle={subtitle} ArtworkBadge={Badge} />
{!sale.isLiveOpen && (
{!sale.isLiveOpenHappened && (
<Flex>
<Flex flexDirection="row" alignItems="center" justifyContent="flex-end">
<Text variant="xs">{sellingPrice}</Text>
Expand Down Expand Up @@ -117,7 +118,7 @@ export const ClosedLotStandingFragmentContainer = createFragmentContainer(Closed
}
}
sale {
isLiveOpen
isLiveOpenHappened
endAt
status
}
Expand Down
48 changes: 31 additions & 17 deletions src/app/Scenes/MyBids/__tests__/ActiveLotStanding.tests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const defaultSaleArtwork = {
},
sale: {
isLiveOpen: false,
isLifeOpenHappend: false,
endAt: "2020-08-05T15:00:00+00:00",
status: "closed",
},
Expand All @@ -34,33 +35,33 @@ const saleArtworkFixture = (overrides = {}) => {
describe(ActiveLotStanding, () => {
describe("User winning status", () => {
it("says 'Highest bid' if the user is winning the lot", () => {
const tree = renderWithWrappersLEGACY(
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: true,
lotState: { reserveStatus: "ReserveMet" },
})}
/>
)
expect(extractText(tree.root)).toContain("Highest bid")
expect(extractText(view.root)).toContain("Highest bid")
})

it("says 'Highest bid' if the user is has the high bid and reserveStatus is UnknownReserve", () => {
const tree = renderWithWrappersLEGACY(
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: true,
lotState: { reserveStatus: "UnknownReserve" },
})}
/>
)
expect(extractText(tree.root)).toContain("Highest bid")
expect(extractText(view.root)).toContain("Highest bid")
})

it("says 'Highest bid' if the user is winning but reserveStatus is ReserveNotMet in auction with Live part", () => {
const date = new Date()
date.setDate(date.getDate() + 1)
const tree = renderWithWrappersLEGACY(
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: true,
Expand All @@ -69,70 +70,83 @@ describe(ActiveLotStanding, () => {
})}
/>
)
expect(extractText(tree.root)).toContain("Highest bid")
expect(extractText(view.root)).toContain("Highest bid")
})

it("hides winning info if auction with Live part are in Live bidding", () => {
const tree = renderWithWrappersLEGACY(
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: true,
sale: { liveStartAt: new Date(), isLiveOpen: true },
sale: { liveStartAt: new Date(), isLiveOpen: true, isLiveOpenHappened: true },
lotState: { reserveStatus: "ReserveNotMet" },
})}
/>
)
expect(extractText(tree.root)).not.toContain("Highest bid")
expect(extractText(view.root)).not.toContain("Highest bid")
})

it("hides winning info if auction with Live part is past open Live bidding time", () => {
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: true,
sale: { liveStartAt: new Date(), isLiveOpen: false, isLiveOpenHappened: true },
lotState: { reserveStatus: "ReserveNotMet" },
})}
/>
)
expect(extractText(view.root)).not.toContain("Highest bid")
})

it("says 'Reserve not met' if the user is winning the lot, but the reserveStatus is ReserveNotMet", () => {
const tree = renderWithWrappersLEGACY(
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: true,
lotState: { reserveStatus: "ReserveNotMet" },
})}
/>
)
expect(extractText(tree.root)).toContain("Reserve not met")
expect(extractText(view.root)).toContain("Reserve not met")
})

it("says 'Outbid' if the user is outbid on the lot, but the reserveStatus is ReserveNotMet", () => {
const tree = renderWithWrappersLEGACY(
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: false,
lotState: { reserveStatus: "ReserveNotMet" },
})}
/>
)
expect(extractText(tree.root)).toContain("Outbid")
expect(extractText(view.root)).toContain("Outbid")
})

it("says 'outbid' if the user is outbid on the lot and reserve is met", () => {
const tree = renderWithWrappersLEGACY(
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: false,
lotState: { reserveStatus: "ReserveMet" },
})}
/>
)
expect(extractText(tree.root)).toContain("Outbid")
expect(extractText(view.root)).toContain("Outbid")
})
})

describe("selling price", () => {
it("shows floor selling price", () => {
const tree = renderWithWrappersLEGACY(
const view = renderWithWrappersLEGACY(
<ActiveLotStanding
saleArtwork={saleArtworkFixture({
isHighestBidder: true,
lotState: { reserveStatus: "ReserveMet" },
})}
/>
)
expect(extractText(tree.root)).toContain("$100")
expect(extractText(view.root)).toContain("$100")
})
})
})
7 changes: 6 additions & 1 deletion src/app/Scenes/Sale/Components/SaleActiveBidItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import {
Outbid,
ReserveNotMet,
BiddingLiveNow,
LiveAuction,
} from "app/Scenes/MyBids/Components/BiddingStatuses"
import { LotFragmentContainer } from "app/Scenes/MyBids/Components/Lot"
// eslint-disable-next-line no-restricted-imports
import { navigate } from "app/system/navigation/navigate"
import { TouchableOpacity } from "react-native"
import { createFragmentContainer, graphql } from "react-relay"
Expand All @@ -32,7 +34,7 @@ export const SaleActiveBidItem: React.FC<SaleActiveBidItemProps> = ({ lotStandin
<Flex flexDirection="row" justifyContent="space-between">
<LotFragmentContainer saleArtwork={saleArtwork} />
<Flex>
{!saleArtwork.sale?.isLiveOpen && (
{!saleArtwork.sale?.isLiveOpenHappened && (
<Flex flexDirection="row" alignItems="center" justifyContent="flex-end">
<Text variant="xs">{sellingPrice}</Text>
<Text variant="xs" color="mono60">
Expand All @@ -44,6 +46,8 @@ export const SaleActiveBidItem: React.FC<SaleActiveBidItemProps> = ({ lotStandin
<Flex flexDirection="row" alignItems="center" justifyContent="flex-end">
{saleArtwork.sale?.isLiveOpen ? (
<BiddingLiveNow />
) : saleArtwork.sale?.isLiveOpenHappened ? (
<LiveAuction />
) : lotStanding?.activeBid?.isWinning &&
saleArtwork.reserveStatus === "ReserveNotMet" ? (
<ReserveNotMet />
Expand Down Expand Up @@ -84,6 +88,7 @@ export const SaleActiveBidItemContainer = createFragmentContainer(SaleActiveBidI
}
sale {
isLiveOpen
isLiveOpenHappened
slug
}
}
Expand Down
Loading