feat: extract dedicated admin listings management screen
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-10 06:28:35 -04:00
parent 54be51ed5d
commit 6c99ffaeba
6 changed files with 74 additions and 30 deletions
+2 -30
View File
@@ -2,7 +2,6 @@
import { computed, onMounted, ref } from "vue";
import { useAuth } from "@/composables/useAuth.js";
import AdminListingStatus from "@/components/molecules/AdminListingStatus.vue";
import InventoryWorkflowStepper from "@/components/molecules/InventoryWorkflowStepper.vue";
import ProductIdentityStep from "@/components/molecules/ProductIdentityStep.vue";
import ProviderMappingStep from "@/components/molecules/ProviderMappingStep.vue";
@@ -26,8 +25,6 @@ import {
import {
createAdminListing,
listAdminListings,
markListingPending,
publishAdminListing,
} from "@/services/admin-listing-service.js";
import {
createAdminSale,
@@ -348,20 +345,6 @@ async function createListing() {
}
}
async function transitionListing(action, id) {
isSaving.value = true;
try {
const listing = await action(id, csrfToken());
listings.value = listings.value.map((candidate) =>
candidate.id === listing.id ? listing : candidate,
);
} catch (requestError) {
error.value = requestError;
} finally {
isSaving.value = false;
}
}
async function recordSale() {
isSaving.value = true;
try {
@@ -1043,19 +1026,8 @@ onMounted(() => {
</section>
<section class="admin-list" aria-labelledby="listings-title">
<h2 id="listings-title">Listings</h2>
<p v-if="!listings.length && !isLoading" class="state state--empty">
No listings yet.
</p>
<div v-else class="admin-listing-stack">
<AdminListingStatus
v-for="listing in listings"
:key="listing.id"
:listing="listing"
:is-saving="isSaving"
@publish="transitionListing(publishAdminListing, listing.id)"
@pending="transitionListing(markListingPending, listing.id)"
/>
</div>
<p>{{ listings.length }} listing{{ listings.length === 1 ? "" : "s" }} recorded.</p>
<a class="button button--secondary" href="/admin/listings">Manage listings</a>
</section>
<section class="admin-list" aria-labelledby="sales-title">
<h2 id="sales-title">Sale history</h2>
@@ -0,0 +1,5 @@
import AdminListingsPage from './AdminListingsPage.vue';
export default { title: 'Pages/AdminListingsPage', component: AdminListingsPage };
export const Listings = {};
+63
View File
@@ -0,0 +1,63 @@
<script setup>
import { onMounted, ref } from 'vue';
import AdminListingStatus from '@/components/molecules/AdminListingStatus.vue';
import { listAdminListings, markListingPending, publishAdminListing } from '@/services/admin-listing-service.js';
const listings = ref([]);
const isLoading = ref(true);
const isSaving = ref(false);
const error = ref('');
function csrfToken() {
return document.cookie.split('; ').find((value) => value.startsWith('pokemon_inventory_csrf='))?.split('=')[1] ?? '';
}
async function load() {
try {
listings.value = await listAdminListings();
} catch (requestError) {
error.value = requestError.message;
} finally {
isLoading.value = false;
}
}
async function transitionListing(action, id) {
isSaving.value = true;
error.value = '';
try {
const listing = await action(id, csrfToken());
listings.value = listings.value.map((candidate) => (candidate.id === listing.id ? listing : candidate));
} catch (requestError) {
error.value = requestError.message;
} finally {
isSaving.value = false;
}
}
onMounted(load);
</script>
<template>
<section class="admin-page">
<p class="catalog-kicker">Inventory management</p>
<h1>Listings</h1>
<p v-if="isLoading" class="state state--loading" role="status">Loading listings...</p>
<p v-if="error" class="state state--error" role="alert">{{ error }}</p>
<section v-if="!isLoading" class="admin-list" aria-labelledby="listings-title">
<h2 id="listings-title">All listings</h2>
<p v-if="!listings.length" class="state state--empty">No listings yet.</p>
<div v-else class="admin-listing-stack">
<AdminListingStatus
v-for="listing in listings"
:key="listing.id"
:listing="listing"
:is-saving="isSaving"
@publish="transitionListing(publishAdminListing, listing.id)"
@pending="transitionListing(markListingPending, listing.id)"
/>
</div>
</section>
</section>
</template>
+1
View File
@@ -19,6 +19,7 @@ async function logout() {
<h1>Inventory</h1>
<nav aria-label="Admin navigation">
<a href="/admin">Overview</a>
<RouterLink :to="{ name: 'admin-listings' }">Listings</RouterLink>
<RouterLink :to="{ name: 'admin-settings' }">Settings</RouterLink>
<RouterLink :to="{ name: 'admin-saved-views' }">Saved views</RouterLink>
</nav>
+2
View File
@@ -10,6 +10,7 @@ import { useAuth } from '@/composables/useAuth.js';
import AdminImagePage from '@/pages/AdminImagePage.vue';
import AdminSettingsPage from '@/pages/AdminSettingsPage.vue';
import AdminSavedViewsPage from '@/pages/AdminSavedViewsPage.vue';
import AdminListingsPage from '@/pages/AdminListingsPage.vue';
const router = createRouter({
history: createWebHistory(),
@@ -24,6 +25,7 @@ const router = createRouter({
children: [
{ path: '', name: 'admin-home', component: AdminHomePage },
{ path: 'inventory/:id/images', name: 'admin-image-manager', component: AdminImagePage },
{ path: 'listings', name: 'admin-listings', component: AdminListingsPage },
{ path: 'settings', name: 'admin-settings', component: AdminSettingsPage },
{ path: 'saved-views', name: 'admin-saved-views', component: AdminSavedViewsPage },
],