feat: add Product tags and bulk tag updates
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-10 00:51:05 -04:00
parent 7c9518923d
commit d6b4dbec5f
22 changed files with 236 additions and 4 deletions
@@ -46,3 +46,14 @@ export const InventoryCsvExport = {
},
},
};
export const BulkTagSelection = {
parameters: {
docs: {
description: {
story:
"The admin workspace supports adding or removing Product tags for selected physical copies.",
},
},
},
};
+45
View File
@@ -13,6 +13,7 @@ import {
listAdminInventory,
listAdminProducts,
refreshAdminInventoryPricing,
updateAdminInventoryTags,
} from "@/services/admin-catalog-service.js";
import {
createAdminListing,
@@ -50,6 +51,7 @@ const { principal } = useAuth();
const products = ref([]);
const inventory = ref([]);
const selectedInventoryIds = ref([]);
const inventoryTags = ref("");
const listings = ref([]);
const sales = ref([]);
const acquisitions = ref([]);
@@ -232,6 +234,26 @@ async function refreshSelectedPricing() {
}
}
async function updateSelectedTags(action) {
const tags = inventoryTags.value.split(",").map((tag) => tag.trim()).filter(Boolean);
if (!selectedInventoryIds.value.length || !tags.length) return;
isSaving.value = true;
try {
await updateAdminInventoryTags(
selectedInventoryIds.value,
action === "add" ? tags : [],
action === "remove" ? tags : [],
csrfToken(),
);
inventoryTags.value = "";
selectedInventoryIds.value = [];
} catch (requestError) {
error.value = requestError;
} finally {
isSaving.value = false;
}
}
async function createListing() {
const item = inventory.value.find(
(candidate) => candidate.id === listingInventoryId.value,
@@ -999,6 +1021,29 @@ onMounted(load);
>
Refresh pricing
</button>
<input
v-model="inventoryTags"
class="fixture-input"
type="text"
placeholder="Tags, comma separated"
aria-label="Tags to add or remove"
/>
<button
class="button button--secondary"
type="button"
:disabled="isSaving || !selectedInventoryIds.length || !inventoryTags.trim()"
@click="updateSelectedTags('add')"
>
Add tags
</button>
<button
class="button button--secondary"
type="button"
:disabled="isSaving || !selectedInventoryIds.length || !inventoryTags.trim()"
@click="updateSelectedTags('remove')"
>
Remove tags
</button>
</div>
<p v-if="!inventory.length && !isLoading" class="state state--empty">
No physical copies yet.
@@ -38,4 +38,12 @@ export function refreshAdminInventoryPricing(inventoryItemIds, providerId, csrfT
headers: { 'x-csrf-token': csrfToken },
body: JSON.stringify({ action: 'PRICING_REFRESH', inventoryItemIds, providerId }),
});
}
export function updateAdminInventoryTags(inventoryItemIds, add, remove, csrfToken) {
return request('/admin/bulk', {
method: 'POST',
headers: { 'x-csrf-token': csrfToken },
body: JSON.stringify({ action: 'TAGS', inventoryItemIds, add, remove }),
});
}