diff --git a/apps/web/src/pages/AdminAcquisitionsPage.stories.js b/apps/web/src/pages/AdminAcquisitionsPage.stories.js new file mode 100644 index 0000000..0153dc9 --- /dev/null +++ b/apps/web/src/pages/AdminAcquisitionsPage.stories.js @@ -0,0 +1,5 @@ +import AdminAcquisitionsPage from './AdminAcquisitionsPage.vue'; + +export default { title: 'Pages/AdminAcquisitionsPage', component: AdminAcquisitionsPage }; + +export const Acquisitions = {}; diff --git a/apps/web/src/pages/AdminAcquisitionsPage.vue b/apps/web/src/pages/AdminAcquisitionsPage.vue new file mode 100644 index 0000000..ad7dfab --- /dev/null +++ b/apps/web/src/pages/AdminAcquisitionsPage.vue @@ -0,0 +1,100 @@ + + + diff --git a/apps/web/src/pages/AdminHomePage.vue b/apps/web/src/pages/AdminHomePage.vue index 7f2dc64..63a0587 100644 --- a/apps/web/src/pages/AdminHomePage.vue +++ b/apps/web/src/pages/AdminHomePage.vue @@ -26,15 +26,6 @@ import { createAdminListing, listAdminListings, } from "@/services/admin-listing-service.js"; -import { - createAdminSale, - listAdminSales, - voidAdminSale, -} from "@/services/admin-sale-service.js"; -import { - createAdminAcquisition, - listAdminAcquisitions, -} from "@/services/admin-acquisition-service.js"; import { getAdminDashboard } from "@/services/admin-dashboard-service.js"; import { addAdminPricingObservation, @@ -71,8 +62,6 @@ const inventoryTags = ref(""); const exportStatus = ref(""); const exportJobId = ref(""); const listings = ref([]); -const sales = ref([]); -const acquisitions = ref([]); const dashboard = ref(null); const valuations = ref({}); const hasValuations = computed(() => Object.keys(valuations.value).length > 0); @@ -116,16 +105,6 @@ const inventoryProductId = ref(""); const conditionId = ref(""); const listingInventoryId = ref(""); const askingPrice = ref(""); -const saleInventoryId = ref(""); -const soldPrice = ref(""); -const costBasis = ref(""); -const saleDate = ref(new Date().toISOString().slice(0, 10)); -const acquisitionInventoryId = ref(""); -const acquisitionMethod = ref("PURCHASE"); -const acquisitionCost = ref(""); -const acquisitionShipping = ref(""); -const acquisitionTax = ref(""); -const acquisitionDate = ref(new Date().toISOString().slice(0, 10)); const pricingInventoryId = ref(""); const pricingProvider = ref("fixture-provider"); const pricingSource = ref(""); @@ -133,7 +112,6 @@ const pricingPrice = ref(""); const overridePrice = ref(""); const overrideReason = ref(""); const refreshStatus = ref(""); -const voidReasons = ref({}); const mappingProductId = ref(""); const mappingProviderId = ref(""); const mappingType = ref("PRODUCT"); @@ -160,8 +138,6 @@ async function load() { categories.value, conditions.value, listings.value, - sales.value, - acquisitions.value, dashboard.value, pricingJobs.value, ] = await Promise.all([ @@ -170,8 +146,6 @@ async function load() { listAdminCategories(), listAdminConditions(), listAdminListings(), - listAdminSales(), - listAdminAcquisitions(), getAdminDashboard(), listAdminPricingJobs(), ]); @@ -345,60 +319,6 @@ async function createListing() { } } -async function recordSale() { - isSaving.value = true; - try { - const sale = await createAdminSale( - { - inventoryItemId: saleInventoryId.value, - costBasisMinor: Number(costBasis.value), - saleDate: saleDate.value, - soldPriceMinor: Number(soldPrice.value), - }, - csrfToken(), - ); - inventory.value = inventory.value.map((item) => - item.id === sale.inventoryItem.id - ? { ...item, status: sale.inventoryItem.status } - : item, - ); - sales.value.unshift(sale); - saleInventoryId.value = ""; - soldPrice.value = ""; - costBasis.value = ""; - } catch (requestError) { - error.value = requestError; - } finally { - isSaving.value = false; - } -} - -async function recordAcquisition() { - isSaving.value = true; - try { - const acquisition = await createAdminAcquisition( - { - inventoryItemId: acquisitionInventoryId.value, - method: acquisitionMethod.value, - costMinor: Number(acquisitionCost.value), - shippingMinor: Number(acquisitionShipping.value || 0), - taxMinor: Number(acquisitionTax.value || 0), - date: acquisitionDate.value, - }, - csrfToken(), - ); - acquisitions.value.unshift(acquisition); - acquisitionInventoryId.value = ""; - acquisitionCost.value = ""; - acquisitionShipping.value = ""; - acquisitionTax.value = ""; - } catch (requestError) { - error.value = requestError; - } finally { - isSaving.value = false; - } -} - async function calculatePricing() { isSaving.value = true; try { @@ -508,30 +428,6 @@ async function overridePricing() { } } -async function voidSale(saleId) { - isSaving.value = true; - try { - const sale = await voidAdminSale( - saleId, - voidReasons.value[saleId] ?? "", - csrfToken(), - ); - sales.value = sales.value.map((candidate) => - candidate.id === sale.id ? sale : candidate, - ); - inventory.value = inventory.value.map((item) => - item.id === sale.inventoryItem.id - ? { ...item, status: sale.inventoryItem.status } - : item, - ); - voidReasons.value = { ...voidReasons.value, [saleId]: "" }; - } catch (requestError) { - error.value = requestError; - } finally { - isSaving.value = false; - } -} - function goToStep(stepId) { if (!WIZARD_STEP_IDS.includes(stepId)) return; activeStepId.value = stepId; @@ -772,131 +668,6 @@ onMounted(() => { -
-
-

Record sale

- - - - - - - - - -
-
-

Record acquisition

- - - - - - - - - - - - - -
-
-

Products

@@ -1030,58 +801,12 @@ onMounted(() => { Manage listings

-

Sale history

-

- No sales recorded yet. -

- +

Sales

+ Manage sales
-

Acquisition history

-

- No acquisitions recorded yet. -

- +

Acquisitions

+ Manage acquisitions
diff --git a/apps/web/src/pages/AdminSalesPage.stories.js b/apps/web/src/pages/AdminSalesPage.stories.js new file mode 100644 index 0000000..4e49be3 --- /dev/null +++ b/apps/web/src/pages/AdminSalesPage.stories.js @@ -0,0 +1,5 @@ +import AdminSalesPage from './AdminSalesPage.vue'; + +export default { title: 'Pages/AdminSalesPage', component: AdminSalesPage }; + +export const Sales = {}; diff --git a/apps/web/src/pages/AdminSalesPage.vue b/apps/web/src/pages/AdminSalesPage.vue new file mode 100644 index 0000000..90d044c --- /dev/null +++ b/apps/web/src/pages/AdminSalesPage.vue @@ -0,0 +1,113 @@ + + + diff --git a/apps/web/src/pages/AdminShell.vue b/apps/web/src/pages/AdminShell.vue index 1de29d1..9bf25f0 100644 --- a/apps/web/src/pages/AdminShell.vue +++ b/apps/web/src/pages/AdminShell.vue @@ -20,6 +20,8 @@ async function logout() { diff --git a/apps/web/src/router/index.js b/apps/web/src/router/index.js index 94f989e..9fc2850 100644 --- a/apps/web/src/router/index.js +++ b/apps/web/src/router/index.js @@ -11,6 +11,8 @@ import AdminImagePage from '@/pages/AdminImagePage.vue'; import AdminSettingsPage from '@/pages/AdminSettingsPage.vue'; import AdminSavedViewsPage from '@/pages/AdminSavedViewsPage.vue'; import AdminListingsPage from '@/pages/AdminListingsPage.vue'; +import AdminSalesPage from '@/pages/AdminSalesPage.vue'; +import AdminAcquisitionsPage from '@/pages/AdminAcquisitionsPage.vue'; const router = createRouter({ history: createWebHistory(), @@ -26,6 +28,8 @@ const router = createRouter({ { path: '', name: 'admin-home', component: AdminHomePage }, { path: 'inventory/:id/images', name: 'admin-image-manager', component: AdminImagePage }, { path: 'listings', name: 'admin-listings', component: AdminListingsPage }, + { path: 'sales', name: 'admin-sales', component: AdminSalesPage }, + { path: 'acquisitions', name: 'admin-acquisitions', component: AdminAcquisitionsPage }, { path: 'settings', name: 'admin-settings', component: AdminSettingsPage }, { path: 'saved-views', name: 'admin-saved-views', component: AdminSavedViewsPage }, ], diff --git a/docs/IMPLEMENTATION-STATUS.md b/docs/IMPLEMENTATION-STATUS.md index ba0c41b..ebdc74c 100644 --- a/docs/IMPLEMENTATION-STATUS.md +++ b/docs/IMPLEMENTATION-STATUS.md @@ -180,6 +180,7 @@ Remaining roadmap areas are approved external provider credentials/adapters, bro - The admin image manager now supports multi-file upload with a drag-and-drop dropzone, per-image alt-text editing, primary selection, up/down reordering, and per-image retry/delete actions targeted at the correct image instead of the first uploaded image. - The guided inventory workflow is now a true single-step-at-a-time wizard: the stepper is clickable, only the active stage's panel is shown, Back/Next controls advance between stages, and the active stage persists across reloads through local storage. - Listing management (publish/mark pending) is now a dedicated `/admin/listings` screen with its own navigation entry; the guided workflow overview links to it and only tracks listing creation and a lightweight count for wizard completion. +- Sale recording/voiding and acquisition recording are now dedicated `/admin/sales` and `/admin/acquisitions` screens with their own navigation entries; the guided workflow overview links to both instead of embedding their forms and history inline. ## Local Commands