feat: add async pricing refresh UI
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-09 14:50:08 -04:00
parent a3f35c8cd1
commit 0e4830ace9
3 changed files with 39 additions and 1 deletions
@@ -74,6 +74,7 @@ export async function registerAdminPricingRoutes(app, options) {
id: job.id, id: job.id,
status: job.status, status: job.status,
error: job.error ?? null, error: job.error ?? null,
valueMinor: job.valuation?.valuation?.valueMinor ?? null,
createdAt: job.createdAt, createdAt: job.createdAt,
updatedAt: job.updatedAt, updatedAt: job.updatedAt,
}, },
+26 -1
View File
@@ -9,7 +9,7 @@ import { createAdminListing, listAdminListings, markListingPending, publishAdmin
import { createAdminSale, listAdminSales, voidAdminSale } from '@/services/admin-sale-service.js'; import { createAdminSale, listAdminSales, voidAdminSale } from '@/services/admin-sale-service.js';
import { createAdminAcquisition, listAdminAcquisitions } from '@/services/admin-acquisition-service.js'; import { createAdminAcquisition, listAdminAcquisitions } from '@/services/admin-acquisition-service.js';
import { getAdminDashboard } from '@/services/admin-dashboard-service.js'; import { getAdminDashboard } from '@/services/admin-dashboard-service.js';
import { addAdminPricingObservation, calculateAdminValuation, overrideAdminValuation } from '@/services/admin-pricing-service.js'; import { addAdminPricingObservation, calculateAdminValuation, enqueueAdminPricingRefresh, getAdminPricingJob, overrideAdminValuation } from '@/services/admin-pricing-service.js';
import { createAdminProviderMapping, listAdminProviderMappings } from '@/services/admin-provider-mapping-service.js'; import { createAdminProviderMapping, listAdminProviderMappings } from '@/services/admin-provider-mapping-service.js';
const { principal } = useAuth(); const { principal } = useAuth();
@@ -48,6 +48,7 @@ const pricingSource = ref('');
const pricingPrice = ref(''); const pricingPrice = ref('');
const overridePrice = ref(''); const overridePrice = ref('');
const overrideReason = ref(''); const overrideReason = ref('');
const refreshStatus = ref('');
const voidReasons = ref({}); const voidReasons = ref({});
const mappingProductId = ref(''); const mappingProductId = ref('');
const mappingProviderId = ref(''); const mappingProviderId = ref('');
@@ -183,6 +184,29 @@ async function calculatePricing() {
finally { isSaving.value = false; } finally { isSaving.value = false; }
} }
async function refreshPricing() {
isSaving.value = true;
refreshStatus.value = 'Queued';
try {
const item = inventory.value.find(({ id }) => id === pricingInventoryId.value);
const job = await enqueueAdminPricingRefresh(item.id, { productId: item.productId, conditionId: item.conditionId }, csrfToken());
for (let attempt = 0; attempt < 15; attempt += 1) {
const status = await getAdminPricingJob(job.id);
refreshStatus.value = status.status;
if (status.status === 'COMPLETED') {
valuations.value = { ...valuations.value, [item.id]: { valueMinor: status.valueMinor, confidence: 'PROVIDER', sourceCount: null, stale: false } };
return;
}
if (status.status === 'FAILED') throw new Error(status.error ?? 'Pricing refresh failed.');
await new Promise((resolve) => setTimeout(resolve, 500));
}
throw new Error('Pricing refresh is taking longer than expected.');
} catch (requestError) {
error.value = requestError;
refreshStatus.value = 'FAILED';
} finally { isSaving.value = false; }
}
async function overridePricing() { async function overridePricing() {
isSaving.value = true; isSaving.value = true;
try { try {
@@ -337,6 +361,7 @@ onMounted(load);
<label class="fixture-label" for="pricing-price">Observed price (cents)</label> <label class="fixture-label" for="pricing-price">Observed price (cents)</label>
<input id="pricing-price" v-model="pricingPrice" class="fixture-input" type="number" min="1" required /> <input id="pricing-price" v-model="pricingPrice" class="fixture-input" type="number" min="1" required />
<button class="button button--primary" type="submit" :disabled="isSaving">Add observation and calculate</button> <button class="button button--primary" type="submit" :disabled="isSaving">Add observation and calculate</button>
<button class="button button--secondary" type="button" :disabled="isSaving" @click="refreshPricing">Refresh from provider<span v-if="refreshStatus"> ({{ refreshStatus }})</span></button>
<label class="fixture-label" for="override-price">Manual value (cents)</label> <label class="fixture-label" for="override-price">Manual value (cents)</label>
<input id="override-price" v-model="overridePrice" class="fixture-input" type="number" min="0" /> <input id="override-price" v-model="overridePrice" class="fixture-input" type="number" min="0" />
<label class="fixture-label" for="override-reason">Override reason</label> <label class="fixture-label" for="override-reason">Override reason</label>
@@ -33,3 +33,15 @@ export function overrideAdminValuation(inventoryItemId, input, csrfToken) {
body: JSON.stringify(input), body: JSON.stringify(input),
}); });
} }
export function enqueueAdminPricingRefresh(inventoryItemId, input, csrfToken) {
return request(`/admin/inventory/${encodeURIComponent(inventoryItemId)}/pricing/refresh`, {
method: 'POST',
headers: { 'x-csrf-token': csrfToken },
body: JSON.stringify(input),
});
}
export function getAdminPricingJob(jobId) {
return request(`/admin/jobs/${encodeURIComponent(jobId)}`);
}