diff --git a/core/static/core/style.scss b/core/static/core/style.scss index 6b037995..f064332a 100644 --- a/core/static/core/style.scss +++ b/core/static/core/style.scss @@ -464,7 +464,7 @@ body { flex-wrap: wrap; $col-gap: 1rem; - $row-gap: 0.5rem; + $row-gap: $col-gap / 3; &.gap { column-gap: $col-gap; diff --git a/counter/schemas.py b/counter/schemas.py index adc8094b..978422a5 100644 --- a/counter/schemas.py +++ b/counter/schemas.py @@ -98,3 +98,5 @@ class ProductFilterSchema(FilterSchema): is_archived: bool | None = Field(None, q="archived") buying_groups: set[int] | None = Field(None, q="buying_groups__in") product_type: set[int] | None = Field(None, q="product_type__in") + club: set[int] | None = Field(None, q="club__in") + counter: set[int] | None = Field(None, q="counters__in") diff --git a/counter/static/bundled/counter/product-list-index.ts b/counter/static/bundled/counter/product-list-index.ts index 70403692..af45a1e3 100644 --- a/counter/static/bundled/counter/product-list-index.ts +++ b/counter/static/bundled/counter/product-list-index.ts @@ -60,6 +60,8 @@ document.addEventListener("alpine:init", () => { productStatus: "" as "active" | "archived" | "both", search: "", productTypes: [] as string[], + clubs: [] as string[], + counters: [] as string[], pageSize: defaultPageSize, page: defaultPage, @@ -67,13 +69,27 @@ document.addEventListener("alpine:init", () => { const url = getCurrentUrlParams(); this.search = url.get("search") || ""; this.productStatus = url.get("productStatus") ?? "active"; - const widget = this.$refs.productTypesInput.widget as TomSelect; - widget.on("change", (items: string[]) => { + const productTypesWidget = this.$refs.productTypesInput.widget as TomSelect; + productTypesWidget.on("change", (items: string[]) => { this.productTypes = [...items]; }); + const clubsWidget = this.$refs.clubsInput.widget as TomSelect; + clubsWidget.on("change", (items: string[]) => { + this.clubs = [...items]; + }); + const countersWidget = this.$refs.countersInput.widget as TomSelect; + countersWidget.on("change", (items: string[]) => { + this.counters = [...items]; + }); await this.load(); - const searchParams = ["search", "productStatus", "productTypes"]; + const searchParams = [ + "search", + "productStatus", + "productTypes", + "clubs", + "counters", + ]; for (const param of searchParams) { this.$watch(param, () => { this.page = defaultPage; @@ -109,6 +125,8 @@ document.addEventListener("alpine:init", () => { is_archived: isArchived, // biome-ignore lint/style/useNamingConvention: api is in snake_case product_type: [...this.productTypes], + club: [...this.clubs], + counter: [...this.counters], }, }; }, @@ -121,14 +139,17 @@ document.addEventListener("alpine:init", () => { const options = this.getQueryParams(); const resp = await productSearchProductsDetailed(options); this.nbPages = Math.ceil(resp.data.count / defaultPageSize); - this.products = resp.data.results.reduce((acc, curr) => { - const key = curr.product_type?.name ?? gettext("Uncategorized"); - if (!(key in acc)) { - acc[key] = []; - } - acc[key].push(curr); - return acc; - }, {}); + this.products = resp.data.results.reduce( + (acc: GroupedProducts, curr: ProductSchema) => { + const key = curr.product_type?.name ?? gettext("Uncategorized"); + if (!(key in acc)) { + acc[key] = []; + } + acc[key].push(curr); + return acc; + }, + {}, + ); this.loading = false; }, diff --git a/counter/templates/counter/product_list.jinja b/counter/templates/counter/product_list.jinja index 0ee8dffa..9644e88f 100644 --- a/counter/templates/counter/product_list.jinja +++ b/counter/templates/counter/product_list.jinja @@ -7,6 +7,7 @@ {% block additional_js %} + {% endblock %} @@ -22,7 +23,6 @@

{% trans %}Filter products{% endtrans %}

-
-
- - - -
+
+
+ + +
+
+ + +
+
+ + +
+

{% trans %}Product list{% endtrans %}