diff --git a/app/frontend/src/javascript/components/store/orders.tsx b/app/frontend/src/javascript/components/store/orders.tsx index b77ca01ab..e3dedb9bc 100644 --- a/app/frontend/src/javascript/components/store/orders.tsx +++ b/app/frontend/src/javascript/components/store/orders.tsx @@ -13,7 +13,7 @@ import { MemberSelect } from '../user/member-select'; import { User } from '../../models/user'; import { FormInput } from '../form/form-input'; import OrderAPI from '../../api/order'; -import { Order, OrderIndexFilter } from '../../models/order'; +import { Order, OrderIndexFilter, OrderSortOption } from '../../models/order'; import { FabPagination } from '../base/fab-pagination'; import { X } from 'phosphor-react'; @@ -27,7 +27,7 @@ interface OrdersProps { * Option format, expected by react-select * @see https://github.com/JedWatson/react-select */ -type selectOption = { value: number, label: string }; +type selectOption = { value: OrderSortOption, label: string }; /** * Option format, expected by checklist @@ -38,7 +38,7 @@ const initFilters: OrderIndexFilter = { reference: '', states: [], page: 1, - sort: 'DESC' + sort: 'created_at-desc' }; const FablabOrdersFilters = 'FablabOrdersFilters'; @@ -126,7 +126,7 @@ const Orders: React.FC = ({ currentUser, onError }) => { return () => { setFilters(draft => { draft.page = 1; - draft.sort = 'DESC'; + draft.sort = 'created_at-desc'; switch (filterType) { case 'reference': draft.reference = ''; @@ -175,8 +175,8 @@ const Orders: React.FC = ({ currentUser, onError }) => { */ const buildOptions = (): Array => { return [ - { value: 0, label: t('app.admin.store.orders.sort.newest') }, - { value: 1, label: t('app.admin.store.orders.sort.oldest') } + { value: 'created_at-desc', label: t('app.admin.store.orders.sort.newest') }, + { value: 'created_at-asc', label: t('app.admin.store.orders.sort.oldest') } ]; }; @@ -185,7 +185,7 @@ const Orders: React.FC = ({ currentUser, onError }) => { */ const handleSorting = (option: selectOption) => { setFilters(draft => { - draft.sort = option.value ? 'ASC' : 'DESC'; + draft.sort = option.value; }); }; @@ -337,7 +337,7 @@ const Orders: React.FC = ({ currentUser, onError }) => {
diff --git a/app/frontend/src/javascript/components/store/products.tsx b/app/frontend/src/javascript/components/store/products.tsx index e5a233cdf..88c93c660 100644 --- a/app/frontend/src/javascript/components/store/products.tsx +++ b/app/frontend/src/javascript/components/store/products.tsx @@ -75,8 +75,7 @@ const Products: React.FC = ({ onSuccess, onError }) => { setProductsCount(data.total_count); return data; } catch (error) { - onError(t('app.admin.store.products.unexpected_error_occurred')); - console.error(error); + onError(t('app.admin.store.products.unexpected_error_occurred') + error); } }; diff --git a/app/frontend/src/javascript/components/store/store-list-header.tsx b/app/frontend/src/javascript/components/store/store-list-header.tsx index 484306f98..85b827761 100644 --- a/app/frontend/src/javascript/components/store/store-list-header.tsx +++ b/app/frontend/src/javascript/components/store/store-list-header.tsx @@ -2,13 +2,13 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import Select from 'react-select'; import Switch from 'react-switch'; -import { ProductSortOption } from '../../models/product'; +import { SortOption } from '../../models/api'; interface StoreListHeaderProps { productsCount: number, selectOptions: selectOption[], onSelectOptionsChange: (option: selectOption) => void, - selectValue?: ProductSortOption, + selectValue?: SortOption, switchLabel?: string, switchChecked?: boolean, onSwitch?: (boolean) => void @@ -17,7 +17,7 @@ interface StoreListHeaderProps { * Option format, expected by react-select * @see https://github.com/JedWatson/react-select */ - type selectOption = { value: ProductSortOption, label: string }; + type selectOption = { value: SortOption, label: string }; /** * Renders an accordion item diff --git a/app/frontend/src/javascript/components/store/store.tsx b/app/frontend/src/javascript/components/store/store.tsx index fa36d21f8..aaa6278cf 100644 --- a/app/frontend/src/javascript/components/store/store.tsx +++ b/app/frontend/src/javascript/components/store/store.tsx @@ -4,7 +4,7 @@ import { react2angular } from 'react2angular'; import { Loader } from '../base/loader'; import { IApplication } from '../../models/application'; import { FabButton } from '../base/fab-button'; -import { Product } from '../../models/product'; +import { Product, ProductSortOption } from '../../models/product'; import { ProductCategory } from '../../models/product-category'; import ProductAPI from '../../api/product'; import ProductCategoryAPI from '../../api/product-category'; @@ -28,7 +28,7 @@ interface StoreProps { * Option format, expected by react-select * @see https://github.com/JedWatson/react-select */ - type selectOption = { value: number, label: string }; + type selectOption = { value: ProductSortOption, label: string }; /** * This component shows public store @@ -51,21 +51,21 @@ const Store: React.FC = ({ onError, onSuccess, currentUser }) => { useEffect(() => { ProductAPI.index({ page: 1, is_active: filterVisible }).then(data => { setPageCount(data.total_pages); - setProducts(data.products); - }).catch(() => { - onError(t('app.public.store.unexpected_error_occurred')); + setProducts(data.data); + }).catch(error => { + onError(t('app.public.store.unexpected_error_occurred') + error); }); ProductCategoryAPI.index().then(data => { setProductCategories(data); formatCategories(data); - }).catch(() => { - onError(t('app.public.store.unexpected_error_occurred')); + }).catch(error => { + onError(t('app.public.store.unexpected_error_occurred') + error); }); MachineAPI.index({ disabled: false }).then(data => { setMachines(buildChecklistOptions(data)); - }).catch(() => { - onError(t('app.public.store.unexpected_error_occurred')); + }).catch(error => { + onError(t('app.public.store.unexpected_error_occurred') + error); }); }, []); @@ -117,10 +117,10 @@ const Store: React.FC = ({ onError, onSuccess, currentUser }) => { */ const buildOptions = (): Array => { return [ - { value: 0, label: t('app.public.store.products.sort.name_az') }, - { value: 1, label: t('app.public.store.products.sort.name_za') }, - { value: 2, label: t('app.public.store.products.sort.price_low') }, - { value: 3, label: t('app.public.store.products.sort.price_high') } + { value: 'name-asc', label: t('app.public.store.products.sort.name_az') }, + { value: 'name-desc', label: t('app.public.store.products.sort.name_za') }, + { value: 'amount-asc', label: t('app.public.store.products.sort.price_low') }, + { value: 'amount-desc', label: t('app.public.store.products.sort.price_high') } ]; }; /** @@ -153,7 +153,7 @@ const Store: React.FC = ({ onError, onSuccess, currentUser }) => { if (page !== currentPage) { ProductAPI.index({ page, is_active: filterVisible }).then(data => { setCurrentPage(page); - setProducts(data.products); + setProducts(data.data); setPageCount(data.total_pages); window.document.getElementById('content-main').scrollTo({ top: 100, behavior: 'smooth' }); }).catch(() => { diff --git a/app/frontend/src/javascript/lib/api.ts b/app/frontend/src/javascript/lib/api.ts index a1ea452f5..6e3abb8e0 100644 --- a/app/frontend/src/javascript/lib/api.ts +++ b/app/frontend/src/javascript/lib/api.ts @@ -1,9 +1,13 @@ +import _ from 'lodash'; import { ApiFilter } from '../models/api'; export default class ApiLib { static filtersToQuery (filters?: ApiFilter): string { if (!filters) return ''; - return '?' + Object.entries(filters).map(f => `${f[0]}=${f[1]}`).join('&'); + return '?' + Object.entries(filters) + .filter(filter => !_.isNil(filter[1])) + .map(filter => `${filter[0]}=${filter[1]}`) + .join('&'); } } diff --git a/app/frontend/src/javascript/lib/product.ts b/app/frontend/src/javascript/lib/product.ts index 56fdfce38..51acb0cef 100644 --- a/app/frontend/src/javascript/lib/product.ts +++ b/app/frontend/src/javascript/lib/product.ts @@ -98,8 +98,8 @@ export default class ProductLib { static indexFiltersToIds = (filters: ProductIndexFilter): ProductIndexFilterIds => { return { ...filters, - categories: filters.categories.map(c => c.id), - machines: filters.machines.map(m => m.id) + categories: filters.categories?.map(c => c.id), + machines: filters.machines?.map(m => m.id) }; }; } diff --git a/app/frontend/src/javascript/models/api.ts b/app/frontend/src/javascript/models/api.ts index 6f531f19d..d4ee0342b 100644 --- a/app/frontend/src/javascript/models/api.ts +++ b/app/frontend/src/javascript/models/api.ts @@ -8,3 +8,5 @@ export interface PaginatedIndex { total_count: number, data: Array } + +export type SortOption = `${string}-${'asc' | 'desc'}` | ''; diff --git a/app/frontend/src/javascript/models/order.ts b/app/frontend/src/javascript/models/order.ts index e1d2fd5e0..421bfa509 100644 --- a/app/frontend/src/javascript/models/order.ts +++ b/app/frontend/src/javascript/models/order.ts @@ -48,6 +48,8 @@ export interface OrderPayment { export type OrderIndex = PaginatedIndex; +export type OrderSortOption = 'created_at-asc' | 'created_at-desc' | ''; + export interface OrderIndexFilter extends ApiFilter { reference?: string, user_id?: number, @@ -56,7 +58,7 @@ export interface OrderIndexFilter extends ApiFilter { name?: string, }, page?: number, - sort?: 'DESC'|'ASC' + sort?: OrderSortOption states?: Array, period_from?: string, period_to?: string diff --git a/app/services/orders/order_service.rb b/app/services/orders/order_service.rb index b5576f0d0..2d724c424 100644 --- a/app/services/orders/order_service.rb +++ b/app/services/orders/order_service.rb @@ -13,7 +13,7 @@ class Orders::OrderService orders = filter_by_period(orders, filters) orders = orders.where.not(state: 'cart') if current_user.member? - orders = orders.order(created_at: filters[:sort] || 'DESC') + orders = orders_ordering(orders, filters) total_count = orders.count orders = orders.page(filters[:page] || 1).per(ORDERS_PER_PAGE) { @@ -106,5 +106,13 @@ class Orders::OrderService orders.where(created_at: DateTime.parse(filters[:period_from])..DateTime.parse(filters[:period_to]).end_of_day) end + + def orders_ordering(orders, filters) + key, order = filters[:sort]&.split('-') + key ||= 'created_at' + order ||= 'desc' + + orders.order(key => order) + end end end diff --git a/app/services/product_service.rb b/app/services/product_service.rb index e40c1a16d..645eb148a 100644 --- a/app/services/product_service.rb +++ b/app/services/product_service.rb @@ -118,7 +118,7 @@ class ProductService end def products_ordering(products, filters) - key, order = filters[:sort].split('-') + key, order = filters[:sort]&.split('-') key ||= 'created_at' order ||= 'desc'