feat: surface listing publication blockers
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-08 23:20:31 -04:00
parent 370e4721ed
commit ea145191df
3 changed files with 20 additions and 5 deletions
@@ -40,7 +40,14 @@ export function createListingService({ listingRepository, inventoryImageReposito
}, },
async list({ sellerId }) { async list({ sellerId }) {
return listingRepository.listBySellerId(sellerId); const listings = await listingRepository.listBySellerId(sellerId);
if (!inventoryImageRepository) return listings;
return Promise.all(listings.map(async (listing) => ({
...listing,
imageCount: await inventoryImageRepository.countByInventoryItemId(listing.inventoryItemId),
inventoryStatus: listing.inventoryStatus ?? 'ACTIVE',
})));
}, },
}; };
} }
@@ -1,11 +1,17 @@
<script setup> <script setup>
defineProps({ import { computed } from 'vue';
const props = defineProps({
listing: { type: Object, required: true }, listing: { type: Object, required: true },
blockers: { type: Array, default: () => [] }, blockers: { type: Array, default: () => [] },
isSaving: { type: Boolean, default: false }, isSaving: { type: Boolean, default: false },
errorMessage: { type: String, default: '' }, errorMessage: { type: String, default: '' },
}); });
defineEmits(['publish', 'pending']); defineEmits(['publish', 'pending']);
const computedBlockers = computed(() => props.blockers.length ? props.blockers : [
...(props.listing.status === 'DRAFT' && props.listing.imageCount < 1 ? ['Add at least one image before publishing.'] : []),
...(props.listing.inventoryStatus === 'SOLD' || props.listing.inventoryStatus === 'ARCHIVED' ? ['Sold or archived inventory cannot be published.'] : []),
]);
</script> </script>
<template> <template>
@@ -16,12 +22,12 @@ defineEmits(['publish', 'pending']);
<span class="fixture-label">${{ (listing.askingPriceMinor / 100).toFixed(2) }}</span> <span class="fixture-label">${{ (listing.askingPriceMinor / 100).toFixed(2) }}</span>
</div> </div>
<span class="status" :class="`status--${listing.status.toLowerCase()}`">{{ listing.status }}</span> <span class="status" :class="`status--${listing.status.toLowerCase()}`">{{ listing.status }}</span>
<ul v-if="blockers.length" class="admin-listing-status__blockers" aria-label="Publication blockers"> <ul v-if="computedBlockers.length" class="admin-listing-status__blockers" aria-label="Publication blockers">
<li v-for="blocker in blockers" :key="blocker">{{ blocker }}</li> <li v-for="blocker in computedBlockers" :key="blocker">{{ blocker }}</li>
</ul> </ul>
<p v-if="errorMessage" class="state state--error" role="alert">{{ errorMessage }}</p> <p v-if="errorMessage" class="state state--error" role="alert">{{ errorMessage }}</p>
<div class="admin-listing-status__actions"> <div class="admin-listing-status__actions">
<button v-if="listing.status === 'DRAFT'" class="button button--primary" type="button" :disabled="isSaving || blockers.length > 0" @click="$emit('publish')">{{ isSaving ? 'Publishing...' : 'Publish' }}</button> <button v-if="listing.status === 'DRAFT'" class="button button--primary" type="button" :disabled="isSaving || computedBlockers.length > 0" @click="$emit('publish')">{{ isSaving ? 'Publishing...' : 'Publish' }}</button>
<button v-if="listing.status === 'DRAFT'" class="button button--secondary" type="button" :disabled="isSaving" @click="$emit('pending')">Mark pending</button> <button v-if="listing.status === 'DRAFT'" class="button button--secondary" type="button" :disabled="isSaving" @click="$emit('pending')">Mark pending</button>
</div> </div>
</article> </article>
+2
View File
@@ -12,6 +12,8 @@ export function toAdminListingDto(listing) {
askingPriceMinor: listing.askingPriceMinor, askingPriceMinor: listing.askingPriceMinor,
status: listing.status, status: listing.status,
notes: listing.notes ?? null, notes: listing.notes ?? null,
imageCount: listing.imageCount ?? 0,
inventoryStatus: listing.inventoryStatus ?? null,
createdAt: toIsoString(listing.createdAt), createdAt: toIsoString(listing.createdAt),
updatedAt: toIsoString(listing.updatedAt), updatedAt: toIsoString(listing.updatedAt),
}; };