feat: improve public listing detail states
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-10 06:09:13 -04:00
parent ae1605c82d
commit 7d20aedd14
6 changed files with 22 additions and 1 deletions
@@ -262,6 +262,7 @@ export function createListingRepository(db) {
conditionCode: result.condition.code, conditionCode: result.condition.code,
marketValueMinor: override?.valueMinor ?? valuation?.valueMinor ?? null, marketValueMinor: override?.valueMinor ?? valuation?.valueMinor ?? null,
marketValueCurrency: override?.currency ?? valuation?.currency ?? null, marketValueCurrency: override?.currency ?? valuation?.currency ?? null,
marketValueUpdatedAt: override?.createdAt ?? valuation?.calculatedAt ?? null,
}; };
}, },
@@ -11,6 +11,7 @@ export const ListingDetail = {
status: "AVAILABLE", status: "AVAILABLE",
priceMinor: 11500, priceMinor: 11500,
marketValueMinor: 12000, marketValueMinor: 12000,
marketValueUpdatedAt: "2026-09-10T00:00:00.000Z",
condition: "Near Mint", condition: "Near Mint",
description: "A clean collector copy with a clear front and back.", description: "A clean collector copy with a clear front and back.",
facts: [ facts: [
@@ -34,3 +35,14 @@ export const ProductWithoutImages = {
facts: [{ label: "Category", value: "Collectible merchandise" }], facts: [{ label: "Category", value: "Collectible merchandise" }],
}, },
}; };
export const SoldListing = {
args: {
title: "Pikachu ex",
status: "SOLD",
priceMinor: 11500,
marketValueMinor: 12000,
marketValueUpdatedAt: "2026-09-10T00:00:00.000Z",
condition: "Near Mint",
},
};
@@ -10,6 +10,7 @@ defineProps({
priceMinor: { type: Number, default: null }, priceMinor: { type: Number, default: null },
marketValueMinor: { type: Number, default: null }, marketValueMinor: { type: Number, default: null },
marketValueCurrency: { type: String, default: "USD" }, marketValueCurrency: { type: String, default: "USD" },
marketValueUpdatedAt: { type: [String, Date], default: null },
condition: { type: String, default: "" }, condition: { type: String, default: "" },
description: { type: String, default: "" }, description: { type: String, default: "" },
facts: { type: Array, default: () => [] }, facts: { type: Array, default: () => [] },
@@ -28,7 +29,7 @@ defineProps({
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<p v-if="description" class="detail-notes">{{ description }}</p> <p v-if="description" class="detail-notes">{{ description }}</p>
<div class="collectible-detail__summary"> <div class="collectible-detail__summary">
<PriceDisplay v-if="priceMinor !== null" :amount-minor="priceMinor" /> <PriceDisplay v-if="priceMinor !== null && status !== 'SOLD'" :amount-minor="priceMinor" />
<PriceDisplay <PriceDisplay
v-if="marketValueMinor !== null" v-if="marketValueMinor !== null"
label="Market value" label="Market value"
@@ -36,6 +37,10 @@ defineProps({
/> />
<ConditionBadge v-if="condition" :label="condition" /> <ConditionBadge v-if="condition" :label="condition" />
</div> </div>
<p v-if="marketValueUpdatedAt" class="collectible-detail__market-updated">
Market value updated
<time :datetime="String(marketValueUpdatedAt)">{{ new Date(marketValueUpdatedAt).toLocaleDateString() }}</time>
</p>
<dl v-if="facts.length" class="product-facts"> <dl v-if="facts.length" class="product-facts">
<div v-for="fact in facts" :key="fact.label"> <div v-for="fact in facts" :key="fact.label">
<dt>{{ fact.label }}</dt> <dt>{{ fact.label }}</dt>
+1
View File
@@ -37,6 +37,7 @@ onMounted(async () => {
:price-minor="listing.askingPriceMinor" :price-minor="listing.askingPriceMinor"
:market-value-minor="listing.marketValueMinor" :market-value-minor="listing.marketValueMinor"
:market-value-currency="listing.marketValueCurrency" :market-value-currency="listing.marketValueCurrency"
:market-value-updated-at="listing.marketValueUpdatedAt"
:condition=" :condition="
listing.conditionName listing.conditionName
? `${listing.conditionName} (${listing.conditionCode})` ? `${listing.conditionName} (${listing.conditionCode})`
+1
View File
@@ -175,6 +175,7 @@ Remaining roadmap areas are approved external provider credentials/adapters, bro
- Review and listing is now an extracted `ReviewListStep` molecule covering physical-copy selection and asking-price confirmation while preserving existing draft creation and publication lifecycle behavior. - Review and listing is now an extracted `ReviewListStep` molecule covering physical-copy selection and asking-price confirmation while preserving existing draft creation and publication lifecycle behavior.
- The guided inventory workflow now has extracted Product, Source Mapping, Physical Copy, Images, Pricing, and Review/List frontend stages, each with colocated Storybook coverage. - The guided inventory workflow now has extracted Product, Source Mapping, Physical Copy, Images, Pricing, and Review/List frontend stages, each with colocated Storybook coverage.
- Public catalog sorting now supports latest manual/calculated market value low-to-high and high-to-low, with null values placed last. - Public catalog sorting now supports latest manual/calculated market value low-to-high and high-to-low, with null values placed last.
- Public listing detail now shows market-value freshness and suppresses asking price for sold listings, with Storybook coverage for sold and valued states.
- Public catalog filters now collapse behind an accessible mobile disclosure, show an active-filter count, and provide a reset action while preserving URL synchronization. - Public catalog filters now collapse behind an accessible mobile disclosure, show an active-filter count, and provide a reset action while preserving URL synchronization.
## Local Commands ## Local Commands
+1
View File
@@ -13,6 +13,7 @@ export function toPublicListingDto(listing) {
askingPriceMinor: listing.askingPriceMinor, askingPriceMinor: listing.askingPriceMinor,
marketValueMinor: listing.marketValueMinor ?? null, marketValueMinor: listing.marketValueMinor ?? null,
marketValueCurrency: listing.marketValueCurrency ?? null, marketValueCurrency: listing.marketValueCurrency ?? null,
marketValueUpdatedAt: toIsoString(listing.marketValueUpdatedAt),
status: listing.status, status: listing.status,
notes: listing.notes ?? null, notes: listing.notes ?? null,
images: listing.images ?? [], images: listing.images ?? [],