feat: add admin catalog workflow
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-08 22:30:30 -04:00
parent 86c60f9381
commit a6d8b9bbf4
6 changed files with 139 additions and 1 deletions
+89 -1
View File
@@ -1,3 +1,91 @@
<script setup>
import { onMounted, ref } from 'vue';
import { RouterLink } from 'vue-router';
import { useAuth } from '@/composables/useAuth.js';
import { createAdminInventory, createAdminProduct, listAdminInventory, listAdminProducts } from '@/services/admin-catalog-service.js';
const { principal } = useAuth();
const products = ref([]);
const inventory = ref([]);
const isLoading = ref(true);
const isSaving = ref(false);
const error = ref(null);
const productName = ref('');
const categoryId = ref('');
const inventoryProductId = ref('');
const conditionId = ref('');
function csrfToken() {
return document.cookie.split('; ').find((value) => value.startsWith('pokemon_inventory_csrf='))?.split('=')[1] ?? '';
}
async function load() {
isLoading.value = true;
try {
[products.value, inventory.value] = await Promise.all([listAdminProducts(), listAdminInventory()]);
} catch (requestError) { error.value = requestError; }
finally { isLoading.value = false; }
}
async function createProduct() {
isSaving.value = true;
try {
const product = await createAdminProduct({ name: productName.value, categoryId: categoryId.value }, csrfToken());
products.value.push(product);
productName.value = '';
} catch (requestError) { error.value = requestError; }
finally { isSaving.value = false; }
}
async function createInventory() {
isSaving.value = true;
try {
const item = await createAdminInventory({ productId: inventoryProductId.value, conditionId: conditionId.value }, csrfToken());
inventory.value.push(item);
} catch (requestError) { error.value = requestError; }
finally { isSaving.value = false; }
}
onMounted(load);
</script>
<template>
<section class="admin-page"><p class="catalog-kicker">Overview</p><h1>Collector workspace</h1><p class="detail-notes">Select an inventory item to manage its images and listing details.</p></section>
<section class="admin-page">
<p class="catalog-kicker">Overview</p>
<h1>Collector workspace</h1>
<p class="detail-notes">{{ principal?.email }} · Build the catalog one physical copy at a time.</p>
<p v-if="isLoading" class="state state--loading" role="status">Loading inventory...</p>
<p v-if="error" class="state state--error" role="alert">{{ error.message }}</p>
<div class="admin-workspace-grid">
<form class="showcase-panel" @submit.prevent="createProduct">
<h2>Add product</h2>
<label class="fixture-label" for="product-name">Name</label>
<input id="product-name" v-model="productName" class="fixture-input" required />
<label class="fixture-label" for="product-category">Category ID</label>
<input id="product-category" v-model="categoryId" class="fixture-input" required />
<button class="button button--primary" type="submit" :disabled="isSaving">Create product</button>
</form>
<form class="showcase-panel" @submit.prevent="createInventory">
<h2>Add inventory copy</h2>
<label class="fixture-label" for="inventory-product">Product ID</label>
<input id="inventory-product" v-model="inventoryProductId" class="fixture-input" required />
<label class="fixture-label" for="inventory-condition">Condition ID</label>
<input id="inventory-condition" v-model="conditionId" class="fixture-input" required />
<button class="button button--primary" type="submit" :disabled="isSaving">Create copy</button>
</form>
</div>
<section class="admin-list" aria-labelledby="products-title">
<h2 id="products-title">Products</h2>
<p v-if="!products.length && !isLoading" class="state state--empty">No products yet.</p>
<ul v-else><li v-for="product in products" :key="product.id">{{ product.name }} <span>{{ product.id }}</span></li></ul>
</section>
<section class="admin-list" aria-labelledby="inventory-title">
<h2 id="inventory-title">Physical inventory</h2>
<p v-if="!inventory.length && !isLoading" class="state state--empty">No physical copies yet.</p>
<ul v-else><li v-for="item in inventory" :key="item.id"><span>{{ item.id }}</span> · {{ item.status }} <RouterLink :to="{ name: 'admin-image-manager', params: { id: item.id } }">Manage images</RouterLink></li></ul>
</section>
</section>
</template>