diff --git a/app/controllers/api/cart_controller.rb b/app/controllers/api/cart_controller.rb index c9de2170d..82cc48615 100644 --- a/app/controllers/api/cart_controller.rb +++ b/app/controllers/api/cart_controller.rb @@ -31,6 +31,12 @@ class API::CartController < API::ApiController render 'api/orders/show' end + def set_offer + authorize @current_order, policy_class: CartPolicy + @order = Cart::SetOfferService.new.call(@current_order, orderable, cart_params[:is_offered]) + render 'api/orders/show' + end + private def orderable diff --git a/app/controllers/api/coupons_controller.rb b/app/controllers/api/coupons_controller.rb index 3d7b37703..8081a7bf7 100644 --- a/app/controllers/api/coupons_controller.rb +++ b/app/controllers/api/coupons_controller.rb @@ -3,7 +3,7 @@ # API Controller for resources of type Coupon # Coupons are used in payments class API::CouponsController < API::ApiController - before_action :authenticate_user! + before_action :authenticate_user!, except: %i[validate] before_action :set_coupon, only: %i[show update destroy] # Number of notifications added to the page when the user clicks on 'load next notifications' @@ -31,18 +31,18 @@ class API::CouponsController < API::ApiController if @coupon.nil? render json: { status: 'rejected' }, status: :not_found else - _user_id = if !current_user.admin? - current_user.id - else + _user_id = if current_user&.admin? params[:user_id] + else + current_user&.id end amount = params[:amount].to_f * 100.0 status = @coupon.status(_user_id, amount) - if status != 'active' - render json: { status: status }, status: :unprocessable_entity - else + if status == 'active' render :validate, status: :ok, location: @coupon + else + render json: { status: status }, status: :unprocessable_entity end end end diff --git a/app/controllers/concerns/api/order_concern.rb b/app/controllers/concerns/api/order_concern.rb index 9e14854dd..6c0b753c1 100644 --- a/app/controllers/concerns/api/order_concern.rb +++ b/app/controllers/concerns/api/order_concern.rb @@ -17,6 +17,6 @@ module API::OrderConcern end def cart_params - params.permit(:order_token, :orderable_id, :quantity, :user_id) + params.permit(:order_token, :orderable_id, :quantity, :user_id, :is_offered) end end diff --git a/app/frontend/src/javascript/api/cart.ts b/app/frontend/src/javascript/api/cart.ts index 4601de322..f0efea8db 100644 --- a/app/frontend/src/javascript/api/cart.ts +++ b/app/frontend/src/javascript/api/cart.ts @@ -22,4 +22,9 @@ export default class CartAPI { const res: AxiosResponse = await apiClient.put('/api/cart/set_quantity', { order_token: order.token, orderable_id: orderableId, quantity }); return res?.data; } + + static async setOffer (order: Order, orderableId: number, isOffered: boolean): Promise { + const res: AxiosResponse = await apiClient.put('/api/cart/set_offer', { order_token: order.token, orderable_id: orderableId, is_offered: isOffered }); + return res?.data; + } } diff --git a/app/frontend/src/javascript/components/cart/cart-button.tsx b/app/frontend/src/javascript/components/cart/cart-button.tsx index 959342044..b9e2b3029 100644 --- a/app/frontend/src/javascript/components/cart/cart-button.tsx +++ b/app/frontend/src/javascript/components/cart/cart-button.tsx @@ -22,13 +22,15 @@ const CartButton: React.FC = () => { * Goto cart page */ const showCart = () => { - window.location.href = '/#!/cart'; + window.location.href = '/#!/store/cart'; }; return (
- {cart?.order_items_attributes?.length} + {cart && cart.order_items_attributes.length > 0 && + {cart.order_items_attributes.length} + }

{t('app.public.cart_button.my_cart')}

); diff --git a/app/frontend/src/javascript/components/cart/store-cart.tsx b/app/frontend/src/javascript/components/cart/store-cart.tsx index a345a8b2d..f385d3821 100644 --- a/app/frontend/src/javascript/components/cart/store-cart.tsx +++ b/app/frontend/src/javascript/components/cart/store-cart.tsx @@ -115,8 +115,12 @@ const StoreCart: React.FC = ({ onSuccess, onError, currentUser, /** * Toggle product offer */ - const onSwitch = (product, checked: boolean) => { - console.log('Offer ', product.orderable_name, ': ', checked); + const toogleProductOffer = (item) => { + return (checked: boolean) => { + CartAPI.setOffer(cart, item.orderable_id, checked).then(data => { + setCart(data); + }).catch(onError); + }; }; /** @@ -128,14 +132,50 @@ const StoreCart: React.FC = ({ onSuccess, onError, currentUser, } }; + /** + * Get the item total + */ + const itemAmount = (item): number => { + return item.quantity * Math.trunc(item.amount * 100) / 100; + }; + + /** + * return true if cart has offered item + */ + const hasOfferedItem = (): boolean => { + return cart.order_items_attributes + .filter(i => i.is_offered).length > 0; + }; + /** * Get the offered item total */ const offeredAmount = (): number => { return cart.order_items_attributes .filter(i => i.is_offered) - .map(i => i.amount) - .reduce((acc, curr) => acc + curr, 0); + .map(i => Math.trunc(i.amount * 100) * i.quantity) + .reduce((acc, curr) => acc + curr, 0) / 100; + }; + + /** + * Get the total amount before offered amount + */ + const totalBeforeOfferedAmount = (): number => { + return (Math.trunc(cart.total * 100) + Math.trunc(offeredAmount() * 100)) / 100; + }; + + /** + * Get the coupon amount + */ + const couponAmount = (): number => { + return (Math.trunc(cart.total * 100) - Math.trunc(computePriceWithCoupon(cart.total, cart.coupon) * 100)) / 100.00; + }; + + /** + * Get the paid total amount + */ + const paidTotal = (): number => { + return computePriceWithCoupon(cart.total, cart.coupon); }; return ( @@ -163,7 +203,7 @@ const StoreCart: React.FC = ({ onSuccess, onError, currentUser,
{t('app.public.store_cart.total')} -

{FormatLib.price(item.quantity * item.amount)}

+

{FormatLib.price(itemAmount(item))}

@@ -175,7 +215,7 @@ const StoreCart: React.FC = ({ onSuccess, onError, currentUser, Offer the product onSwitch(item, checked)} + onChange={toogleProductOffer(item)} width={40} height={19} uncheckedIcon={false} @@ -194,7 +234,7 @@ const StoreCart: React.FC = ({ onSuccess, onError, currentUser,

[TODO: texte venant des paramètres de la boutique…]

- {cart && !cartIsEmpty() && cart.user && + {cart && !cartIsEmpty() &&
@@ -203,7 +243,7 @@ const StoreCart: React.FC = ({ onSuccess, onError, currentUser,