1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-30 19:52:20 +01:00

(merge) branch 'product-store_integration'

This commit is contained in:
vincent 2022-09-08 14:06:38 +02:00
commit 9e4d6028c5
14 changed files with 229 additions and 47 deletions

View File

@ -9,7 +9,6 @@ interface FabStateLabelProps {
* Render a label preceded by a bot
*/
export const FabStateLabel: React.FC<FabStateLabelProps> = ({ status, background, children }) => {
console.log('status: ', status);
return (
<span className={`fab-state-label ${status !== undefined ? status : ''} ${background ? 'bg' : ''}`}>
{children}

View File

@ -4,12 +4,15 @@ import { useTranslation } from 'react-i18next';
import { react2angular } from 'react2angular';
import { Loader } from '../base/loader';
import { IApplication } from '../../models/application';
import { useForm } from 'react-hook-form';
import { FabButton } from '../base/fab-button';
import { StoreListHeader } from './store-list-header';
import { AccordionItem } from './accordion-item';
import { OrderItem } from './order-item';
import { MemberSelect } from '../user/member-select';
import { User } from '../../models/user';
import { FormInput } from '../form/form-input';
import { TDateISODate } from '../../typings/date-iso';
declare const Application: IApplication;
@ -35,9 +38,10 @@ type checklistOption = { value: number, label: string };
// TODO: delete next eslint disable
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Orders: React.FC<OrdersProps> = ({ currentUser, onSuccess, onError }) => {
console.log('currentUser: ', currentUser);
const { t } = useTranslation('admin');
const { register, getValues } = useForm();
const [filters, setFilters] = useImmer<Filters>(initFilters);
const [clearFilters, setClearFilters] = useState<boolean>(false);
const [accordion, setAccordion] = useState({});
@ -119,6 +123,13 @@ const Orders: React.FC<OrdersProps> = ({ currentUser, onSuccess, onError }) => {
});
};
/**
* Filter: by period
*/
const handlePeriod = () => {
console.log(getValues(['period_from', 'period_to']));
};
/**
* Open/close accordion items
*/
@ -186,6 +197,27 @@ const Orders: React.FC<OrdersProps> = ({ currentUser, onSuccess, onError }) => {
</div>
</div>
</AccordionItem>
<AccordionItem id={3}
isOpen={accordion[3]}
onChange={handleAccordion}
label={t('app.admin.store.orders.filter_period')}
>
<div className='content'>
<div className="group">
<div className="period">
from
<FormInput id="period_from"
register={register}
type="date" />
to
<FormInput id="period_to"
register={register}
type="date" />
</div>
<FabButton onClick={handlePeriod} className="is-info">{t('app.admin.store.orders.filter_apply')}</FabButton>
</div>
</div>
</AccordionItem>
</div>
</div>
@ -216,11 +248,15 @@ Application.Components.component('orders', react2angular(OrdersWrapper, ['curren
interface Filters {
reference: string,
status: checklistOption[],
memberId: number
memberId: number,
period_from: TDateISODate,
period_to: TDateISODate
}
const initFilters: Filters = {
reference: '',
status: [],
memberId: null
memberId: null,
period_from: null,
period_to: null
};

View File

@ -1,26 +1,76 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { IApplication } from '../../models/application';
import { User } from '../../models/user';
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';
declare const Application: IApplication;
interface ShowOrderProps {
orderRef: string,
currentUser?: User,
onError: (message: string) => void,
onSuccess: (message: string) => void
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, 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> = ({ orderRef, onError, onSuccess }) => {
const { t } = useTranslation('admin');
export const ShowOrder: React.FC<ShowOrderProps> = ({ orderRef, currentUser, onError, onSuccess }) => {
const { t } = useTranslation('shared');
/**
* Check if the current operator has administrative rights or is a normal member
*/
const isPrivileged = (): boolean => {
return (currentUser?.role === 'admin' || currentUser?.role === 'manager');
};
/**
* Creates sorting options to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
return [
{ value: 0, label: t('app.shared.store.show_order.status.error') },
{ value: 1, label: t('app.shared.store.show_order.status.canceled') },
{ value: 2, label: t('app.shared.store.show_order.status.pending') },
{ value: 3, label: t('app.shared.store.show_order.status.under_preparation') },
{ value: 4, label: t('app.shared.store.show_order.status.paid') },
{ value: 5, label: t('app.shared.store.show_order.status.ready') },
{ value: 6, label: t('app.shared.store.show_order.status.collected') },
{ value: 7, label: t('app.shared.store.show_order.status.refunded') }
];
};
/**
* Callback after selecting an action
*/
const handleAction = (action: selectOption) => {
console.log('Action:', action);
};
// Styles the React-select component
const customStyles = {
control: base => ({
...base,
width: '20ch',
backgroundColor: 'transparent'
}),
indicatorSeparator: () => ({
display: 'none'
})
};
/**
* Returns a className according to the status
@ -43,28 +93,37 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderRef, onError, onSucce
<header>
<h2>[order.ref]</h2>
<div className="grpBtn">
{isPrivileged() &&
<Select
options={buildOptions()}
onChange={option => handleAction(option)}
styles={customStyles}
/>
}
<a href={''}
target='_blank'
className='fab-button is-black'
rel='noreferrer'>
{t('app.admin.store.show_order.see_invoice')}
{t('app.shared.store.show_order.see_invoice')}
</a>
</div>
</header>
<div className="client-info">
<label>{t('app.admin.store.show_order.client')}</label>
<label>{t('app.shared.store.show_order.tracking')}</label>
<div className="content">
{isPrivileged() &&
<div className='group'>
<span>{t('app.shared.store.show_order.client')}</span>
<p>order.user.name</p>
</div>
}
<div className='group'>
<span>{t('app.admin.store.show_order.client')}</span>
<p>order.user.name</p>
</div>
<div className='group'>
<span>{t('app.admin.store.show_order.created_at')}</span>
<span>{t('app.shared.store.show_order.created_at')}</span>
<p>order.created_at</p>
</div>
<div className='group'>
<span>{t('app.admin.store.show_order.last_update')}</span>
<span>{t('app.shared.store.show_order.last_update')}</span>
<p>order.???</p>
</div>
<FabStateLabel status={statusColor('error')} background>
@ -74,7 +133,7 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderRef, onError, onSucce
</div>
<div className="cart">
<label>{t('app.admin.store.show_order.cart')}</label>
<label>{t('app.shared.store.show_order.cart')}</label>
<div>
{/* loop sur les articles du panier */}
<article className='store-cart-list-item'>
@ -82,19 +141,19 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderRef, onError, onSucce
<img alt=''src={noImage} />
</div>
<div className="ref">
<span>{t('app.admin.store.show_order.reference_short')} orderable_id?</span>
<span>{t('app.shared.store.show_order.reference_short')} orderable_id?</span>
<p>o.orderable_name</p>
</div>
<div className="actions">
<div className='price'>
<p>o.amount</p>
<span>/ {t('app.admin.store.show_order.unit')}</span>
<span>/ {t('app.shared.store.show_order.unit')}</span>
</div>
<span className="count">o.quantity</span>
<div className='total'>
<span>{t('app.admin.store.show_order.item_total')}</span>
<span>{t('app.shared.store.show_order.item_total')}</span>
<p>o.quantity * o.amount</p>
</div>
</div>
@ -104,15 +163,15 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderRef, onError, onSucce
<div className="subgrid">
<div className="payment-info">
<label>{t('app.admin.store.show_order.payment_informations')}</label>
<label>{t('app.shared.store.show_order.payment_informations')}</label>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ipsum rerum commodi quaerat possimus! Odit, harum.</p>
</div>
<div className="amount">
<label>{t('app.admin.store.show_order.amount')}</label>
<p>{t('app.admin.store.show_order.products_total')}<span>order.amount</span></p>
<p className='gift'>{t('app.admin.store.show_order.gift_total')}<span>-order.amount</span></p>
<p>{t('app.admin.store.show_order.coupon')}<span>order.amount</span></p>
<p className='total'>{t('app.admin.store.show_order.total')} <span>order.total</span></p>
<label>{t('app.shared.store.show_order.amount')}</label>
<p>{t('app.shared.store.show_order.products_total')}<span>order.amount</span></p>
<p className='gift'>{t('app.shared.store.show_order.gift_total')}<span>-order.amount</span></p>
<p>{t('app.shared.store.show_order.coupon')}<span>order.amount</span></p>
<p className='total'>{t('app.shared.store.show_order.cart_total')} <span>order.total</span></p>
</div>
</div>
</div>
@ -127,4 +186,4 @@ const ShowOrderWrapper: React.FC<ShowOrderProps> = (props) => {
);
};
Application.Components.component('showOrder', react2angular(ShowOrderWrapper, ['orderRef', 'onError', 'onSuccess']));
Application.Components.component('showOrder', react2angular(ShowOrderWrapper, ['orderRef', 'currentUser', 'onError', 'onSuccess']));

View File

@ -0,0 +1,52 @@
/* eslint-disable
no-return-assign,
no-undef,
*/
'use strict';
Application.Controllers.controller('AdminShowOrdersController', ['$rootScope', '$scope', 'CSRF', 'growl', '$state', '$transition$',
function ($rootScope, $scope, CSRF, growl, $state, $transition$) {
/* PRIVATE SCOPE */
/* PUBLIC SCOPE */
$scope.orderToken = $transition$.params().token;
/**
* Callback triggered in case of error
*/
$scope.onError = (message) => {
growl.error(message);
};
/**
* Callback triggered in case of success
*/
$scope.onSuccess = (message) => {
growl.success(message);
};
/**
* Click Callback triggered in case of back products list
*/
$scope.backProductsList = () => {
$state.go('app.admin.store.orders');
};
// currently logged-in user
$scope.currentUser = $rootScope.currentUser;
/* PRIVATE SCOPE */
/**
* Kind of constructor: these actions will be realized first when the controller is loaded
*/
const initialize = function () {
// set the authenticity tokens in the forms
CSRF.setMetaTags();
};
// init the controller (call at the end !)
return initialize();
}
]);

View File

@ -29,7 +29,7 @@ Application.Controllers.controller('ShowOrdersController', ['$rootScope', '$scop
* Click Callback triggered in case of back products list
*/
$scope.backProductsList = () => {
$state.go('app.admin.store.orders');
$state.go('app.logged.dashboard.orders');
};
// currently logged-in user

View File

@ -930,7 +930,7 @@ angular.module('application.router', ['ui.router'])
views: {
'main@': {
templateUrl: '/admin/orders/show.html',
controller: 'ShowOrdersController'
controller: 'AdminShowOrdersController'
}
}
})

View File

@ -35,6 +35,7 @@
}
.date { @include text-sm; }
.price {
flex: 0 1 30%;
display: flex;
flex-direction: column;
justify-self: flex-end;

View File

@ -143,4 +143,10 @@
}
}
}
.period {
display: flex;
justify-content: center;
align-items: center;
}
}

View File

@ -12,8 +12,8 @@
<div class="show-order-nav">
<a class="back-btn" ng-click="backProductsList()" tabindex="0">
<i class="fas fa-angle-left"></i>
<span translate>{{ 'app.admin.store.back_products_list' }}</span>
<span translate>{{ 'app.admin.store.back_to_list' }}</span>
</a>
</div>
<show-order order-token="orderToken" on-error="onError" on-success="onSuccess" />
<show-order current-user="currentUser" order-ref="orderRef" on-error="onError" on-success="onSuccess" />
</section>

View File

@ -12,7 +12,7 @@
<div class="edit-product-nav">
<a class="back-btn" ng-click="backProductsList()">
<i class="fas fa-angle-left"></i>
<span translate>{{ 'app.admin.store.back_products_list' }}</span>
<span translate>{{ 'app.admin.store.back_to_list' }}</span>
</a>
</div>
<edit-product product-id="productId" on-success="onSuccess" on-error="onError"/>

View File

@ -12,7 +12,7 @@
<div class="new-product-nav">
<a class="back-btn" ng-click="backProductsList()" tabindex="0">
<i class="fas fa-angle-left"></i>
<span translate>{{ 'app.admin.store.back_products_list' }}</span>
<span translate>{{ 'app.admin.store.back_to_list' }}</span>
</a>
</div>
<new-product on-success="onSuccess" on-error="onError"/>

View File

@ -0,0 +1,17 @@
<div>
<section class="heading">
<div class="row no-gutter">
<ng-include src="'/dashboard/nav.html'"></ng-include>
</div>
</section>
<section>
<div class="show-order-nav">
<a class="back-btn" ng-click="backProductsList()" tabindex="0">
<i class="fas fa-angle-left"></i>
<span translate>{{ 'app.shared.store.show_order.back_to_list' }}</span>
</a>
</div>
<show-order current-user="currentUser" order-token="orderToken" on-error="onError" on-success="onSuccess" />
</section>
</div>

View File

@ -1899,7 +1899,7 @@ en:
all_products: "All products"
categories_of_store: "Store's categories"
the_orders: "Orders"
back_products_list: "Back to products list"
back_to_list: "Back to list"
product_categories:
title: "Categories"
info: "<strong>Information:</strong></br>Find below all the categories created. The categories are arranged on two levels maximum, you can arrange them with a drag and drop. The order of the categories will be identical on the public view and the list below. Please note that you can delete a category or a sub-category even if they are associated with products. The latter will be left without categories. If you delete a category that contains sub-categories, the latter will also be deleted. <strong>Make sure that your categories are well arranged and save your choice.</strong>"
@ -2037,6 +2037,7 @@ en:
filter_ref: "By reference"
filter_status: "By status"
filter_client: "By client"
filter_period: "By period"
status:
error: "Payment error"
canceled: "Canceled"
@ -2049,21 +2050,6 @@ en:
sort:
newest: "Newest first"
oldest: "Oldest first"
show_order:
see_invoice: "See invoice"
client: "Client"
created_at: "Creation date"
last_update: "Last update"
cart: "Cart"
reference_short: "ref:"
unit: "Unit"
item_total: "Total"
payment_informations : "Payment informations"
amount: "Amount"
products_total: "Products total"
gift_total: "Discount total"
coupon: "Coupon"
cart_total: "Cart total"
store_settings:
title: 'Settings'
withdrawal_instructions: 'Product withdrawal instructions'

View File

@ -564,3 +564,29 @@ en:
order_item:
total: "Total"
client: "Client"
show_order:
back_to_list: "Back to list"
see_invoice: "See invoice"
tracking: "Order tracking"
client: "Client"
created_at: "Creation date"
last_update: "Last update"
cart: "Cart"
reference_short: "ref:"
unit: "Unit"
item_total: "Total"
payment_informations : "Payment informations"
amount: "Amount"
products_total: "Products total"
gift_total: "Discount total"
coupon: "Coupon"
cart_total: "Cart total"
status:
error: "Payment error"
canceled: "Canceled"
pending: "Pending payment"
under_preparation: "Under preparation"
paid: "Paid"
ready: "Ready"
collected: "Collected"
refunded: "Refunded"