From f015e23a85642a8371b6c4324acfbe21a2d2ec9a Mon Sep 17 00:00:00 2001 From: Du Peng Date: Thu, 15 Sep 2022 20:19:19 +0200 Subject: [PATCH] (wip) change order state by admin --- app/controllers/api/orders_controller.rb | 9 ++-- app/exceptions/update_order_state_error.rb | 3 ++ app/frontend/src/javascript/api/order.ts | 7 ++- .../components/store/show-order.tsx | 48 ++++++++++++++----- app/frontend/src/javascript/lib/order.ts | 4 ++ .../stylesheets/modules/store/order-item.scss | 2 + .../src/stylesheets/modules/store/orders.scss | 2 + app/models/notification_type.rb | 2 + app/models/order.rb | 1 + app/models/order_activity.rb | 11 +++++ app/services/orders/cancel_order_service.rb | 18 +++++++ app/services/orders/order_ready_service.rb | 18 +++++++ app/services/orders/order_service.rb | 6 +++ app/services/orders/refund_order_service.rb | 0 .../orders/set_in_progress_service.rb | 13 +++++ app/services/payments/payment_concern.rb | 1 + ...otify_user_order_is_canceled.json.jbuilder | 2 + .../_notify_user_order_is_ready.json.jbuilder | 2 + .../notify_user_order_is_canceled.erb | 5 ++ .../notify_user_order_is_ready.erb | 8 ++++ config/locales/en.yml | 4 ++ config/locales/mails.en.yml | 8 ++++ .../20220915133100_create_order_activities.rb | 12 +++++ db/schema.rb | 15 +++++- 24 files changed, 180 insertions(+), 21 deletions(-) create mode 100644 app/exceptions/update_order_state_error.rb create mode 100644 app/models/order_activity.rb create mode 100644 app/services/orders/cancel_order_service.rb create mode 100644 app/services/orders/order_ready_service.rb create mode 100644 app/services/orders/refund_order_service.rb create mode 100644 app/services/orders/set_in_progress_service.rb create mode 100644 app/views/api/notifications/_notify_user_order_is_canceled.json.jbuilder create mode 100644 app/views/api/notifications/_notify_user_order_is_ready.json.jbuilder create mode 100644 app/views/notifications_mailer/notify_user_order_is_canceled.erb create mode 100644 app/views/notifications_mailer/notify_user_order_is_ready.erb create mode 100644 db/migrate/20220915133100_create_order_activities.rb diff --git a/app/controllers/api/orders_controller.rb b/app/controllers/api/orders_controller.rb index 096e0df82..1a33e70a7 100644 --- a/app/controllers/api/orders_controller.rb +++ b/app/controllers/api/orders_controller.rb @@ -15,11 +15,8 @@ class API::OrdersController < API::ApiController def update authorize @order - if @order.update(order_parameters) - render status: :ok - else - render json: @order.errors.full_messages, status: :unprocessable_entity - end + @order = ::Orders::OrderService.update_state(@order, current_user, order_params[:state], order_params[:note]) + render :show end def destroy @@ -35,6 +32,6 @@ class API::OrdersController < API::ApiController end def order_params - params.require(:order).permit(:state) + params.require(:order).permit(:state, :note) end end diff --git a/app/exceptions/update_order_state_error.rb b/app/exceptions/update_order_state_error.rb new file mode 100644 index 000000000..d21610051 --- /dev/null +++ b/app/exceptions/update_order_state_error.rb @@ -0,0 +1,3 @@ +# Raised when update order state error +class UpdateOrderStateError < StandardError +end diff --git a/app/frontend/src/javascript/api/order.ts b/app/frontend/src/javascript/api/order.ts index 7203899a4..aee7b888a 100644 --- a/app/frontend/src/javascript/api/order.ts +++ b/app/frontend/src/javascript/api/order.ts @@ -3,7 +3,7 @@ import { AxiosResponse } from 'axios'; import { Order, OrderIndexFilter, OrderIndex } from '../models/order'; import ApiLib from '../lib/api'; -export default class ProductAPI { +export default class OrderAPI { static async index (filters?: OrderIndexFilter): Promise { const res: AxiosResponse = await apiClient.get(`/api/orders${ApiLib.filtersToQuery(filters)}`); return res?.data; @@ -13,4 +13,9 @@ export default class ProductAPI { const res: AxiosResponse = await apiClient.get(`/api/orders/${id}`); return res?.data; } + + static async updateState (order: Order, state: string, note?: string): Promise { + const res: AxiosResponse = await apiClient.patch(`/api/orders/${order.id}`, { order: { state, note } }); + return res?.data; + } } diff --git a/app/frontend/src/javascript/components/store/show-order.tsx b/app/frontend/src/javascript/components/store/show-order.tsx index 0e84cae01..de409c8c0 100644 --- a/app/frontend/src/javascript/components/store/show-order.tsx +++ b/app/frontend/src/javascript/components/store/show-order.tsx @@ -24,7 +24,7 @@ interface ShowOrderProps { * Option format, expected by react-select * @see https://github.com/JedWatson/react-select */ -type selectOption = { value: number, label: string }; +type selectOption = { value: string, label: string }; /** * This component shows an order details @@ -35,11 +35,12 @@ export const ShowOrder: React.FC = ({ orderId, currentUser, onEr const { t } = useTranslation('shared'); const [order, setOrder] = useState(); + const [currentAction, setCurrentAction] = useState(); useEffect(() => { OrderAPI.get(orderId).then(data => { setOrder(data); - }); + }).catch(onError); }, []); /** @@ -53,23 +54,43 @@ export const ShowOrder: React.FC = ({ orderId, currentUser, onEr * Creates sorting options to the react-select format */ const buildOptions = (): Array => { - return [ - { value: 0, label: t('app.shared.store.show_order.state.error') }, - { value: 1, label: t('app.shared.store.show_order.state.canceled') }, - { value: 2, label: t('app.shared.store.show_order.state.pending') }, - { value: 3, label: t('app.shared.store.show_order.state.under_preparation') }, - { value: 4, label: t('app.shared.store.show_order.state.paid') }, - { value: 5, label: t('app.shared.store.show_order.state.ready') }, - { value: 6, label: t('app.shared.store.show_order.state.collected') }, - { value: 7, label: t('app.shared.store.show_order.state.refunded') } - ]; + let actions = []; + switch (order.state) { + case 'paid': + actions = actions.concat(['in_progress', 'ready', 'canceled', 'refunded']); + break; + case 'payment_failed': + actions = actions.concat(['canceled']); + break; + case 'in_progress': + actions = actions.concat(['ready', 'canceled', 'refunded']); + break; + case 'ready': + actions = actions.concat(['canceled', 'refunded']); + break; + case 'canceled': + actions = actions.concat(['refunded']); + break; + default: + actions = []; + } + return actions.map(action => { + return { value: action, label: t(`app.shared.store.show_order.state.${action}`) }; + }); }; /** * Callback after selecting an action */ const handleAction = (action: selectOption) => { - console.log('Action:', action); + setCurrentAction(action); + OrderAPI.updateState(order, action.value, action.value).then(data => { + setOrder(data); + setCurrentAction(null); + }).catch((e) => { + onError(e); + setCurrentAction(null); + }); }; // Styles the React-select component @@ -127,6 +148,7 @@ export const ShowOrder: React.FC = ({ orderId, currentUser, onEr