feat: expand catalog sorting
Quality / test (push) Canceled after 0s

This commit is contained in:
2026-09-10 02:39:42 -04:00
parent 0008af9f2c
commit b036092775
8 changed files with 67 additions and 11 deletions
@@ -135,7 +135,13 @@ export function createListingRepository(db) {
const offset = (page - 1) * limit;
const sortOrder =
sort === "price_low"
sort === "updated"
? desc(listings.updatedAt)
: sort === "name_asc"
? products.name
: sort === "name_desc"
? desc(products.name)
: sort === "price_low"
? listings.askingPriceMinor
: sort === "price_high"
? desc(listings.askingPriceMinor)
@@ -58,8 +58,8 @@ export function createSearchQuery({
throw new Error("minPriceMinor must be <= maxPriceMinor");
}
if (!["recent", "price_low", "price_high"].includes(sort)) {
throw new Error("sort must be recent, price_low, or price_high");
if (!["recent", "updated", "name_asc", "name_desc", "price_low", "price_high"].includes(sort)) {
throw new Error("sort must be recent, updated, name_asc, name_desc, price_low, or price_high");
}
return {
@@ -57,6 +57,9 @@ test("builds a public query filter set that hides sold items by default", () =>
test("accepts only the supported public search sort values", () => {
assert.equal(createSearchQuery({ sort: "price_low" }).sort, "price_low");
assert.equal(createSearchQuery({ sort: "updated" }).sort, "updated");
assert.equal(createSearchQuery({ sort: "name_asc" }).sort, "name_asc");
assert.equal(createSearchQuery({ sort: "name_desc" }).sort, "name_desc");
assert.throws(
() => createSearchQuery({ sort: "random" }),
/sort must be recent/,
@@ -0,0 +1,18 @@
.catalog-sort-select {
display: grid;
gap: var(--space-1);
}
.catalog-sort-select span {
color: var(--color-text-muted);
font-size: var(--text-sm);
}
.catalog-sort-select select {
min-height: 44px;
border: var(--border-thin) solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-2) var(--space-3);
background: var(--color-surface);
color: var(--color-text);
}
@@ -0,0 +1,6 @@
import CatalogSortSelect from './CatalogSortSelect.vue';
export default { title: 'Atoms/CatalogSortSelect', component: CatalogSortSelect };
export const RecentlyAdded = { args: { modelValue: 'recent' } };
export const NameAscending = { args: { modelValue: 'name_asc' } };
@@ -0,0 +1,28 @@
<script setup>
defineProps({
modelValue: { type: String, required: true },
});
defineEmits(['update:modelValue']);
</script>
<template>
<label class="catalog-sort-select">
<span>Sort</span>
<select
:value="modelValue"
@change="$emit('update:modelValue', $event.target.value)"
>
<option value="recent">Recently added</option>
<option value="updated">Recently updated</option>
<option value="name_asc">Name A-Z</option>
<option value="name_desc">Name Z-A</option>
<option value="price_low">Price: low to high</option>
<option value="price_high">Price: high to low</option>
</select>
</label>
</template>
<style scoped lang="scss">
@use './CatalogSortSelect.scss';
</style>
+2 -8
View File
@@ -8,6 +8,7 @@ import {
listPublicConditions,
} from "@/services/catalog-service.js";
import ListingCard from "@/components/molecules/ListingCard.vue";
import CatalogSortSelect from "@/components/atoms/CatalogSortSelect.vue";
const route = useRoute();
const router = useRouter();
@@ -124,14 +125,7 @@ watch(
>Max price (cents)
<input v-model.number="maxPriceMinor" type="number" min="0"
/></label>
<label
>Sort
<select v-model="sort">
<option value="recent">Recently added</option>
<option value="price_low">Price: low to high</option>
<option value="price_high">Price: high to low</option>
</select></label
>
<CatalogSortSelect v-model="sort" />
</div>
</header>
+1
View File
@@ -163,6 +163,7 @@ Remaining roadmap areas are approved external provider credentials/adapters, bro
- `npm audit --omit=dev` currently reports zero production vulnerabilities. Re-run the audit after dependency changes and review development-dependency findings separately before production releases.
- Authenticated E2E coverage now verifies both the core catalog workflow and the Settings, Saved Views, and export-controls workflow.
- Production dependency audit currently reports zero vulnerabilities with `npm audit --omit=dev`.
- Public catalog sorting now supports recently added, recently updated, name A-Z, name Z-A, price low-to-high, and price high-to-low through a URL-synchronized atomic sort control. Broader inventory filter parity and market-value sorting remain.
## Local Commands