mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 00:53:08 +00:00 
			
		
		
		
	Make products filterable by product type
This commit is contained in:
		| @@ -4,9 +4,11 @@ import type { TomOption } from "tom-select/dist/types/types"; | ||||
| import type { escape_html } from "tom-select/dist/types/utils"; | ||||
| import { | ||||
|   type CounterSchema, | ||||
|   type ProductTypeSchema, | ||||
|   type SimpleProductSchema, | ||||
|   counterSearchCounter, | ||||
|   productSearchProducts, | ||||
|   producttypeFetchAll, | ||||
| } from "#openapi"; | ||||
|  | ||||
| @registerComponent("product-ajax-select") | ||||
| @@ -34,6 +36,37 @@ export class ProductAjaxSelect extends AjaxSelect { | ||||
|   } | ||||
| } | ||||
|  | ||||
| @registerComponent("product-type-ajax-select") | ||||
| export class ProductTypeAjaxSelect extends AjaxSelect { | ||||
|   protected valueField = "id"; | ||||
|   protected labelField = "name"; | ||||
|   protected searchField = ["name"]; | ||||
|   private productTypes = null as ProductTypeSchema[]; | ||||
|  | ||||
|   protected async search(query: string): Promise<TomOption[]> { | ||||
|     // The production database has a grand total of 26 product types | ||||
|     // and the filter logic is really simple. | ||||
|     // Thus, it's appropriate to fetch all product types during first use, | ||||
|     // then to reuse the result again and again. | ||||
|     if (this.productTypes === null) { | ||||
|       this.productTypes = (await producttypeFetchAll()).data || null; | ||||
|     } | ||||
|     return this.productTypes.filter((t) => | ||||
|       t.name.toLowerCase().includes(query.toLowerCase()), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   protected renderOption(item: ProductTypeSchema, sanitize: typeof escape_html) { | ||||
|     return `<div class="select-item"> | ||||
|             <span class="select-item-text">${sanitize(item.name)}</span> | ||||
|           </div>`; | ||||
|   } | ||||
|  | ||||
|   protected renderItem(item: ProductTypeSchema, sanitize: typeof escape_html) { | ||||
|     return `<span>${sanitize(item.name)}</span>`; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @registerComponent("counter-ajax-select") | ||||
| export class CounterAjaxSelect extends AjaxSelect { | ||||
|   protected valueField = "id"; | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import { csv } from "#core:utils/csv"; | ||||
| import { History, getCurrentUrlParams, updateQueryString } from "#core:utils/history"; | ||||
| import type { NestedKeyOf } from "#core:utils/types"; | ||||
| import { showSaveFilePicker } from "native-file-system-adapter"; | ||||
| import type TomSelect from "tom-select"; | ||||
| import { | ||||
|   type ProductSchema, | ||||
|   type ProductSearchProductsDetailedData, | ||||
| @@ -58,6 +59,7 @@ document.addEventListener("alpine:init", () => { | ||||
|  | ||||
|     productStatus: "" as "active" | "archived" | "both", | ||||
|     search: "", | ||||
|     productTypes: [] as string[], | ||||
|     pageSize: defaultPageSize, | ||||
|     page: defaultPage, | ||||
|  | ||||
| @@ -65,17 +67,22 @@ 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[]) => { | ||||
|         this.productTypes = [...items]; | ||||
|       }); | ||||
|  | ||||
|       await this.load(); | ||||
|       for (const param of ["search", "productStatus"]) { | ||||
|       const searchParams = ["search", "productStatus", "productTypes"]; | ||||
|       for (const param of searchParams) { | ||||
|         this.$watch(param, () => { | ||||
|           this.page = defaultPage; | ||||
|         }); | ||||
|       } | ||||
|       for (const param of ["search", "productStatus", "page"]) { | ||||
|       for (const param of [...searchParams, "page"]) { | ||||
|         this.$watch(param, async (value: string) => { | ||||
|           updateQueryString(param, value, History.Replace); | ||||
|           this.nbPages = 0; | ||||
|           this.products = {}; | ||||
|           await this.load(); | ||||
|         }); | ||||
|       } | ||||
| @@ -100,6 +107,8 @@ document.addEventListener("alpine:init", () => { | ||||
|           search: search, | ||||
|           // biome-ignore lint/style/useNamingConvention: api is in snake_case | ||||
|           is_archived: isArchived, | ||||
|           // biome-ignore lint/style/useNamingConvention: api is in snake_case | ||||
|           product_type: this.productTypes, | ||||
|         }, | ||||
|       }; | ||||
|     }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user