1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-18 07:52:23 +01:00

(feat) add order action ready/in_progress/canceled

This commit is contained in:
Du Peng 2022-09-16 11:38:11 +02:00
parent f015e23a85
commit 53004767bf
4 changed files with 162 additions and 72 deletions

View File

@ -0,0 +1,129 @@
import React, { useState, BaseSyntheticEvent } from 'react';
import { useTranslation } from 'react-i18next';
import Select from 'react-select';
import { FabModal } from '../base/fab-modal';
import OrderAPI from '../../api/order';
import { Order } from '../../models/order';
interface OrderActionsProps {
order: Order,
onSuccess: (order: Order, message: string) => void,
onError: (message: string) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: string, label: string };
/**
* Actions for an order
*/
export const OrderActions: React.FC<OrderActionsProps> = ({ order, onSuccess, onError }) => {
const { t } = useTranslation('shared');
const [currentAction, setCurrentAction] = useState<selectOption>();
const [modalIsOpen, setModalIsOpen] = useState<boolean>(false);
const [readyNote, setReadyNote] = useState<string>('');
// Styles the React-select component
const customStyles = {
control: base => ({
...base,
width: '20ch',
backgroundColor: 'transparent'
}),
indicatorSeparator: () => ({
display: 'none'
})
};
/**
* Close the action confirmation modal
*/
const closeModal = (): void => {
setModalIsOpen(false);
setCurrentAction(null);
};
/**
* Creates sorting options to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
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.order_actions.state.${action}`) };
});
};
/**
* Callback after selecting an action
*/
const handleAction = (action: selectOption) => {
setCurrentAction(action);
setModalIsOpen(true);
};
/**
* Callback after confirm an action
*/
const handleActionConfirmation = () => {
OrderAPI.updateState(order, currentAction.value, readyNote).then(data => {
onSuccess(data, t(`app.shared.store.order_actions.order_${currentAction.value}_success`));
setCurrentAction(null);
setModalIsOpen(false);
}).catch((e) => {
onError(e);
setCurrentAction(null);
setModalIsOpen(false);
});
};
return (
<>
<Select
options={buildOptions()}
onChange={option => handleAction(option)}
value={currentAction}
styles={customStyles}
/>
<FabModal title={t('app.shared.store.order_actions.confirmation_required')}
isOpen={modalIsOpen}
toggleModal={closeModal}
closeButton={true}
confirmButton={t('app.shared.store.order_actions.confirm')}
onConfirm={handleActionConfirmation}
className="order-actions-confirmation-modal">
<p>{t(`app.shared.store.order_actions.confirm_order_${currentAction?.value}`)}</p>
{currentAction?.value === 'ready' &&
<textarea
id="order-ready-note"
value={readyNote}
placeholder={t('app.shared.store.order_actions.order_ready_note')}
onChange={(e: BaseSyntheticEvent) => setReadyNote(e.target.value)}
style={{ width: '100%' }}
rows={5} />
}
</FabModal>
</>
);
};

View File

@ -6,36 +6,28 @@ import { react2angular } from 'react2angular';
import { Loader } from '../base/loader';
import noImage from '../../../../images/no_image.png';
import { FabStateLabel } from '../base/fab-state-label';
import Select from 'react-select';
import OrderAPI from '../../api/order';
import { Order } from '../../models/order';
import FormatLib from '../../lib/format';
import OrderLib from '../../lib/order';
import { OrderActions } from './order-actions';
declare const Application: IApplication;
interface ShowOrderProps {
orderId: string,
currentUser?: User,
onSuccess: (message: string) => void,
onError: (message: string) => void,
onSuccess: (message: string) => void
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: string, label: string };
/**
* This component shows an order details
*/
// TODO: delete next eslint disable
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const ShowOrder: React.FC<ShowOrderProps> = ({ orderId, currentUser, onError, onSuccess }) => {
export const ShowOrder: React.FC<ShowOrderProps> = ({ orderId, currentUser, onSuccess, onError }) => {
const { t } = useTranslation('shared');
const [order, setOrder] = useState<Order>();
const [currentAction, setCurrentAction] = useState<selectOption>();
useEffect(() => {
OrderAPI.get(orderId).then(data => {
@ -50,61 +42,6 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderId, currentUser, onEr
return (currentUser?.role === 'admin' || currentUser?.role === 'manager');
};
/**
* Creates sorting options to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
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) => {
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
const customStyles = {
control: base => ({
...base,
width: '20ch',
backgroundColor: 'transparent'
}),
indicatorSeparator: () => ({
display: 'none'
})
};
/**
* Returns order's payment info
*/
@ -135,6 +72,14 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderId, currentUser, onEr
return paymentVerbose;
};
/**
* Callback after action success
*/
const handleActionSuccess = (data: Order, message: string) => {
setOrder(data);
onSuccess(message);
};
if (!order) {
return null;
}
@ -145,12 +90,7 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderId, currentUser, onEr
<h2>[{order.reference}]</h2>
<div className="grpBtn">
{isPrivileged() &&
<Select
options={buildOptions()}
onChange={option => handleAction(option)}
value={currentAction}
styles={customStyles}
/>
<OrderActions order={order} onSuccess={handleActionSuccess} onError={onError} />
}
{order?.invoice_id && (
<a href={`/api/invoices/${order?.invoice_id}/download`}

View File

@ -8,6 +8,9 @@ class Orders::CancelOrderService
order.state = 'canceled'
ActiveRecord::Base.transaction do
activity = order.order_activities.create(activity_type: 'canceled', operator_profile_id: current_user.invoicing_profile.id)
order.order_items.each do |item|
ProductService.update_stock(item.orderable, 'external', 'cancelled_by_customer', item.quantity, item.id)
end
order.save
NotificationCenter.call type: 'notify_user_order_is_canceled',
receiver: order.statistic_profile.user,

View File

@ -605,3 +605,21 @@ en:
on_DATE_at_TIME: "on {DATE} at {TIME},"
for_an_amount_of_AMOUNT: "for an amount of {AMOUNT}"
and: 'and'
order_actions:
state:
cart: 'Cart'
in_progress: 'In progress'
paid: "Paid"
payment_failed: "Payment error"
canceled: "Canceled"
ready: "Ready"
refunded: "Refunded"
confirm: 'Confirm'
confirmation_required: "Confirmation required"
confirm_order_in_progress: "This order is in the process of being prepared ?"
order_in_progress_success: "Order is under preparation"
confirm_order_ready: "This order is ready ?"
order_ready_note: ''
order_ready_success: "Order is ready"
confirm_order_canceled: "Do you want to cancel this order ?"
order_canceled_success: "Order is canceled"