feat: add listing publication readiness
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-08 23:18:11 -04:00
parent 4e0e4ebd48
commit 370e4721ed
4 changed files with 58 additions and 1 deletions
@@ -0,0 +1,19 @@
import { expect, userEvent, within } from '@storybook/test';
import AdminListingStatus from './AdminListingStatus.vue';
const listing = { id: 'listing-1', productName: 'Charizard ex #199/165', askingPriceMinor: 11500, status: 'DRAFT' };
export default { title: 'Molecules/AdminListingStatus', component: AdminListingStatus };
export const DraftReady = { args: { listing, blockers: [] } };
export const DraftBlockedByImages = { args: { listing, blockers: ['Add at least one completed image before publishing.'] } };
export const Publishing = { args: { listing, blockers: [], isSaving: true } };
export const PublishError = { args: { listing, blockers: [], errorMessage: 'Listing requirements are not met.' } };
export const KeyboardPublish = {
args: { listing, blockers: [] },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole('button', { name: 'Publish' }));
await expect(canvas.getByRole('button', { name: 'Publish' })).toBeInTheDocument();
},
};
@@ -0,0 +1,36 @@
<script setup>
defineProps({
listing: { type: Object, required: true },
blockers: { type: Array, default: () => [] },
isSaving: { type: Boolean, default: false },
errorMessage: { type: String, default: '' },
});
defineEmits(['publish', 'pending']);
</script>
<template>
<article class="admin-listing-status">
<div class="admin-listing-status__identity">
<p class="catalog-kicker">Listing</p>
<h3>{{ listing.productName }}</h3>
<span class="fixture-label">${{ (listing.askingPriceMinor / 100).toFixed(2) }}</span>
</div>
<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">
<li v-for="blocker in blockers" :key="blocker">{{ blocker }}</li>
</ul>
<p v-if="errorMessage" class="state state--error" role="alert">{{ errorMessage }}</p>
<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--secondary" type="button" :disabled="isSaving" @click="$emit('pending')">Mark pending</button>
</div>
</article>
</template>
<style scoped lang="scss">
.admin-listing-status { display: grid; grid-template-columns: 1fr auto; gap: var(--space-3); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg); padding: var(--space-4); background: var(--color-surface); }
.admin-listing-status h3 { font-size: var(--text-lg); }
.admin-listing-status__identity { display: grid; gap: var(--space-2); }
.admin-listing-status__blockers { grid-column: 1 / -1; margin: 0; padding-left: var(--space-6); color: var(--color-warning-text); }
.admin-listing-status__actions { display: flex; grid-column: 1 / -1; flex-wrap: wrap; gap: var(--space-2); }
</style>