1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-20 14:54:15 +01:00

(wip) Admin orders list

This commit is contained in:
vincent 2022-08-29 19:15:10 +02:00
parent 78683a31b3
commit 2e696f94fd
11 changed files with 237 additions and 32 deletions

View File

@ -0,0 +1,177 @@
import React, { useState, useEffect } from 'react';
import { useImmer } from 'use-immer';
import { useTranslation } from 'react-i18next';
import { react2angular } from 'react2angular';
import { Loader } from '../base/loader';
import { IApplication } from '../../models/application';
import { FabButton } from '../base/fab-button';
import { StoreListHeader } from './store-list-header';
import { AccordionItem } from './accordion-item';
declare const Application: IApplication;
interface OrdersProps {
onSuccess: (message: string) => void,
onError: (message: string) => void,
}
/**
* Option format, expected by react-select
* @see https://github.com/JedWatson/react-select
*/
type selectOption = { value: number, label: string };
/**
* Option format, expected by checklist
*/
type checklistOption = { value: number, label: string };
const statusOptions: checklistOption[] = [
{ value: 0, label: 'cart' },
{ value: 1, label: 'paid by credit card' },
{ value: 2, label: 'paid in cash' },
{ value: 3, label: 'being processed' },
{ value: 4, label: 'ready' },
{ value: 5, label: 'delivered' },
{ value: 6, label: 'canceled' },
{ value: 7, label: 'refunded' }
];
/**
* Admin list of orders
*/
const Orders: React.FC<OrdersProps> = ({ onSuccess, onError }) => {
const { t } = useTranslation('admin');
const [filters, setFilters] = useImmer<Filters>(initFilters);
const [clearFilters, setClearFilters] = useState<boolean>(false);
const [accordion, setAccordion] = useState({});
useEffect(() => {
applyFilters();
setClearFilters(false);
}, [clearFilters]);
/**
* Create a new order
*/
const newOrder = () => {
console.log('Create new order');
};
/**
* Apply filters
*/
const applyFilters = () => {
console.log('Apply filters:', filters);
};
/**
* Clear filters
*/
const clearAllFilters = () => {
setFilters(initFilters);
setClearFilters(true);
console.log('Clear all filters');
};
/**
* Creates sorting options to the react-select format
*/
const buildOptions = (): Array<selectOption> => {
return [
{ value: 0, label: t('app.admin.store.orders.sort.newest') },
{ value: 1, label: t('app.admin.store.orders.sort.oldest') }
];
};
/**
* Display option: sorting
*/
const handleSorting = (option: selectOption) => {
console.log('Sort option:', option);
};
/**
* Filter: by status
*/
const handleSelectStatus = (s: checklistOption, checked) => {
const list = [...filters.status];
checked
? list.push(s)
: list.splice(list.indexOf(s), 1);
setFilters(draft => {
return { ...draft, status: list };
});
};
/**
* Open/close accordion items
*/
const handleAccordion = (id, state) => {
setAccordion({ ...accordion, [id]: state });
};
return (
<div className='orders'>
<header>
<h2>{t('app.admin.store.orders.heading')}</h2>
<div className='grpBtn'>
<FabButton className="main-action-btn" onClick={newOrder}>{t('app.admin.store.orders.create_order')}</FabButton>
</div>
</header>
<div className="store-filters">
<header>
<h3>{t('app.admin.store.orders.filter')}</h3>
<div className='grpBtn'>
<FabButton onClick={clearAllFilters} className="is-black">{t('app.admin.store.orders.filter_clear')}</FabButton>
</div>
</header>
<div className="accordion">
<AccordionItem id={0}
isOpen={accordion[0]}
onChange={handleAccordion}
label={t('app.admin.store.orders.filter_status')}
>
<div className='content'>
<div className="list u-scrollbar">
{statusOptions.map(s => (
<label key={s.value}>
<input type="checkbox" checked={filters.status.includes(s)} onChange={(event) => handleSelectStatus(s, event.target.checked)} />
<p>{s.label}</p>
</label>
))}
</div>
<FabButton onClick={applyFilters} className="is-info">{t('app.admin.store.orders.filter_apply')}</FabButton>
</div>
</AccordionItem>
</div>
</div>
<div className="store-list">
<StoreListHeader
productsCount={0}
selectOptions={buildOptions()}
onSelectOptionsChange={handleSorting}
/>
</div>
</div>
);
};
const OrdersWrapper: React.FC<OrdersProps> = (props) => {
return (
<Loader>
<Orders {...props} />
</Loader>
);
};
Application.Components.component('orders', react2angular(OrdersWrapper, ['onSuccess', 'onError']));
interface Filters {
reference: string,
status: checklistOption[]
}
const initFilters: Filters = {
reference: '',
status: []
};

View File

@ -13,7 +13,7 @@ import ProductCategoryAPI from '../../api/product-category';
import MachineAPI from '../../api/machine';
import { AccordionItem } from './accordion-item';
import { X } from 'phosphor-react';
import { ProductsListHeader } from './products-list-header';
import { StoreListHeader } from './store-list-header';
declare const Application: IApplication;
@ -274,8 +274,8 @@ const Products: React.FC<ProductsProps> = ({ onSuccess, onError }) => {
</AccordionItem>
</div>
</div>
<div className='store-products-list'>
<ProductsListHeader
<div className='store-list'>
<StoreListHeader
productsCount={filteredProductsList.length}
selectOptions={buildOptions()}
onSelectOptionsChange={handleSorting}

View File

@ -3,13 +3,13 @@ import { useTranslation } from 'react-i18next';
import Select from 'react-select';
import Switch from 'react-switch';
interface ProductsListHeaderProps {
interface StoreListHeaderProps {
productsCount: number,
selectOptions: selectOption[],
onSelectOptionsChange: (option: selectOption) => void,
switchLabel?: string,
switchChecked: boolean,
onSwitch: (boolean) => void
switchChecked?: boolean,
onSwitch?: (boolean) => void
}
/**
* Option format, expected by react-select
@ -20,7 +20,7 @@ interface ProductsListHeaderProps {
/**
* Renders an accordion item
*/
export const ProductsListHeader: React.FC<ProductsListHeaderProps> = ({ productsCount, selectOptions, onSelectOptionsChange, switchLabel, switchChecked, onSwitch }) => {
export const StoreListHeader: React.FC<StoreListHeaderProps> = ({ productsCount, selectOptions, onSelectOptionsChange, switchLabel, switchChecked, onSwitch }) => {
const { t } = useTranslation('admin');
// Styles the React-select component
@ -37,32 +37,34 @@ export const ProductsListHeader: React.FC<ProductsListHeaderProps> = ({ products
};
return (
<div className='products-list-header'>
<div className='store-list-header'>
<div className='count'>
<p>{t('app.admin.store.products_list_header.result_count')}<span>{productsCount}</span></p>
<p>{t('app.admin.store.store_list_header.result_count')}<span>{productsCount}</span></p>
</div>
<div className="display">
<div className='sort'>
<p>{t('app.admin.store.products_list_header.display_options')}</p>
<p>{t('app.admin.store.store_list_header.display_options')}</p>
<Select
options={selectOptions}
onChange={evt => onSelectOptionsChange(evt)}
styles={customStyles}
/>
</div>
<div className='visibility'>
<label>
<span>{switchLabel || t('app.admin.store.products_list_header.visible_only')}</span>
<Switch
checked={switchChecked}
onChange={(checked) => onSwitch(checked)}
width={40}
height={19}
uncheckedIcon={false}
checkedIcon={false}
handleDiameter={15} />
</label>
</div>
{onSwitch &&
<div className='visibility'>
<label>
<span>{switchLabel || t('app.admin.store.store_list_header.visible_only')}</span>
<Switch
checked={switchChecked}
onChange={(checked) => onSwitch(checked)}
width={40}
height={19}
uncheckedIcon={false}
checkedIcon={false}
handleDiameter={15} />
</label>
</div>
}
</div>
</div>
);

View File

@ -14,7 +14,7 @@ import useCart from '../../hooks/use-cart';
import { emitCustomEvent } from 'react-custom-events';
import { User } from '../../models/user';
import { AccordionItem } from './accordion-item';
import { ProductsListHeader } from './products-list-header';
import { StoreListHeader } from './store-list-header';
declare const Application: IApplication;
@ -213,8 +213,8 @@ const Store: React.FC<StoreProps> = ({ onError, currentUser }) => {
</div>
</div>
</aside>
<div className='store-products-list'>
<ProductsListHeader
<div className='store-list'>
<StoreListHeader
productsCount={products.length}
selectOptions={buildOptions()}
onSelectOptionsChange={handleSorting}

View File

@ -92,14 +92,15 @@
@import "modules/settings/user-validation-setting";
@import "modules/socials/fab-socials";
@import "modules/store/_utilities";
@import "modules/store/orders";
@import "modules/store/product-categories";
@import "modules/store/product-form";
@import "modules/store/products-grid";
@import "modules/store/products-list-header";
@import "modules/store/products-list";
@import "modules/store/products";
@import "modules/store/store-filters";
@import "modules/store/store-products-list";
@import "modules/store/store-list-header";
@import "modules/store/store-list";
@import "modules/store/store";
@import "modules/subscriptions/free-extend-modal";
@import "modules/subscriptions/renew-modal";

View File

@ -0,0 +1,14 @@
.orders {
max-width: 1600px;
margin: 0 auto;
padding-bottom: 6rem;
@include grid-col(12);
gap: 3.2rem;
align-items: flex-start;
header {
@include header();
padding-bottom: 0;
grid-column: 1 / -1;
}
}

View File

@ -10,6 +10,7 @@
header {
@include header();
padding-bottom: 0;
grid-column: 1 / -1;
}
}

View File

@ -1,4 +1,4 @@
.products-list-header {
.store-list-header {
padding: 0.8rem 2.4rem;
display: flex;
justify-content: space-between;

View File

@ -1,4 +1,4 @@
.store-products-list {
.store-list {
grid-column: 4 / -1;
display: grid;
grid-template-columns: 1fr;

View File

@ -1 +1 @@
<h2>Orders page</h2>
<orders on-success="onSuccess" on-error="onError"/>

View File

@ -1947,7 +1947,7 @@ en:
name_za: "Z-A"
price_low: "Price: low to high"
price_high: "Price: high to low"
products_list_header:
store_list_header:
result_count: "Result count:"
display_options: "Display options:"
visible_only: "Visible products only"
@ -1987,3 +1987,13 @@ en:
product_images_info: "<strong>Advice</strong></br>We advise you to use a square format, jpg or png, for jpgs, please use white for the background colour. The main visual will be the visual presented first in the product sheet."
add_product_image: "Add an image"
save: "Save"
orders:
heading: "Orders"
create_order: "Create an order"
filter: "Filter"
filter_clear: "Clear all"
filter_apply: "Apply"
filter_status: "By status"
sort:
newest: "Newest first"
oldest: "Oldest first"