feat: style item form.

This commit is contained in:
Ahmed Bouhuolia
2020-04-12 20:29:10 +02:00
parent 4d4191dfc0
commit 2e7e18bb97
4 changed files with 263 additions and 177 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useMemo, useCallback } from 'react';
import * as Yup from 'yup';
import { useFormik } from 'formik';
import {
@@ -7,7 +7,8 @@ import {
Intent,
InputGroup,
HTMLSelect,
Button
Button,
Classes,
} from '@blueprintjs/core';
import { Row, Col } from 'react-grid-system';
import { Select } from '@blueprintjs/select';
@@ -15,6 +16,10 @@ import AppToaster from 'components/AppToaster';
import AccountsConnect from 'connectors/Accounts.connector';
import ItemsConnect from 'connectors/Items.connect';
import {compose} from 'utils';
import ErrorMessage from 'components/ErrorMessage';
import classNames from 'classnames';
import Icon from 'components/Icon';
const ItemForm = ({
requestSubmitItem,
@@ -22,17 +27,16 @@ const ItemForm = ({
}) => {
const [selectedAccounts, setSelectedAccounts] = useState({});
const ItemTypeDisplay = [
const ItemTypeDisplay = useMemo(() => ([
{ value: null, label: 'Select Item Type' },
{ value: 'service', label: 'Service' },
{ value: 'inventory', label: 'Inventory' },
{ value: 'non-inventory', label: 'Non-Inventory' }
];
]), []);
const validationSchema = Yup.object().shape({
name: Yup.string().required(),
type: Yup.string()
.trim()
.required(),
type: Yup.string().trim().required(),
sku: Yup.string().required(),
cost_price: Yup.number().required(),
sell_price: Yup.number().required(),
@@ -42,22 +46,39 @@ const ItemForm = ({
category_id: Yup.number().required(),
stock: Yup.string() || Yup.boolean()
});
const initialValues = useMemo(() => ({
name: '',
type: '',
sku: '',
cost_price: null,
sell_price: null,
cost_account_id: null,
sell_account_id: null,
inventory_account_id: null,
category_id: null,
note: '',
}), []);
const formik = useFormik({
enableReinitialize: true,
validationSchema: validationSchema,
initialValues: {},
onSubmit: values => {
requestSubmitItem(values)
.then(response => {
AppToaster.show({
message: 'The_Items_has_been_Submit'
});
})
.catch(err => {
alert(err.message);
initialValues: {
...initialValues,
},
onSubmit: (values, { setSubmitting }) => {
requestSubmitItem(values).then((response) => {
AppToaster.show({
message: 'The_Items_has_been_Submit'
});
setSubmitting(false);
})
.catch((error) => {
setSubmitting(false);
});
}
});
const {errors, values, touched} = useMemo(() => formik, [formik]);
const accountItem = (item, { handleClick }) => (
<MenuItem key={item.id} text={item.name} label={item.code} onClick={handleClick} />
@@ -73,131 +94,130 @@ const ItemForm = ({
}
};
const onItemAccountSelect = filedName => {
return account => {
const onItemAccountSelect = useCallback((filedName) => {
return (account) => {
setSelectedAccounts({
...selectedAccounts,
[filedName]: account
});
formik.setFieldValue(filedName, account.id);
};
};
}, [formik, selectedAccounts]);
const getSelectedAccountLabel = (fieldName, defaultLabel) => {
const getSelectedAccountLabel = useCallback((fieldName, defaultLabel) => {
return typeof selectedAccounts[fieldName] !== 'undefined'
? selectedAccounts[fieldName].name
: defaultLabel;
};
? selectedAccounts[fieldName].name : defaultLabel;
}, [selectedAccounts]);
const requiredSpan = useMemo(() => (<span class="required">*</span>), []);
const infoIcon = useMemo(() => (<Icon icon="info-circle" iconSize={12} />), []);
return (
<div class='item-form'>
<form onSubmit={formik.handleSubmit}>
<FormGroup
medium={true}
label={'Item Type'}
className={'form-group--item-type'}
intent={formik.errors.type && Intent.DANGER}
helperText={formik.errors.type && formik.errors.type}
inline={true}
>
<HTMLSelect
fill={true}
options={ItemTypeDisplay}
{...formik.getFieldProps('type')}
/>
</FormGroup>
<FormGroup
label={'Item Name'}
className={'form-group--item-name'}
intent={formik.errors.name && Intent.DANGER}
helperText={formik.errors.name && formik.errors.name}
inline={true}
>
<InputGroup
<div class="item-form__primary-section">
<FormGroup
medium={true}
intent={formik.errors.name && Intent.DANGER}
{...formik.getFieldProps('name')}
/>
</FormGroup>
<FormGroup
label={'SKU'}
className={'form-group--item-sku'}
intent={formik.errors.sku && Intent.DANGER}
helperText={formik.errors.sku && formik.errors.sku}
inline={true}
>
<InputGroup
medium={true}
intent={formik.errors.sku && Intent.DANGER}
{...formik.getFieldProps('sku')}
/>
</FormGroup>
<FormGroup
label={'Category'}
className={'form-group--item-category'}
inline={true}
intent={formik.errors.category_id && Intent.DANGER}
helperText={formik.errors.category_id && formik.errors.category_id}
>
<Select
items={accounts}
itemRenderer={accountItem}
itemPredicate={filterAccounts}
popoverProps={{ minimal: true }}
onItemSelect={onItemAccountSelect('category_id')}
label={'Item Type'}
labelInfo={requiredSpan}
className={'form-group--item-type'}
intent={(errors.type && touched.type) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="type" />}
inline={true}
>
<Button
<HTMLSelect
fill={true}
rightIcon='caret-down'
text={getSelectedAccountLabel('category_id', 'Select account')}
options={ItemTypeDisplay}
{...formik.getFieldProps('type')}
/>
</Select>
</FormGroup>
</FormGroup>
<FormGroup
label={'Item Name'}
labelInfo={requiredSpan}
className={'form-group--item-name'}
intent={(errors.name && touched.name) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="name" />}
inline={true}
>
<InputGroup
medium={true}
intent={(errors.name && touched.name) && Intent.DANGER}
{...formik.getFieldProps('name')}
/>
</FormGroup>
<FormGroup
label={'SKU'}
labelInfo={infoIcon}
className={'form-group--item-sku'}
intent={(errors.sku && touched.sku) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="sku" />}
inline={true}
>
<InputGroup
medium={true}
intent={(errors.sku && touched.sku) && Intent.DANGER}
{...formik.getFieldProps('sku')}
/>
</FormGroup>
<FormGroup
label={'Category'}
labelInfo={infoIcon}
inline={true}
intent={(errors.category_id && touched.category_id) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="category" />}
className={classNames(
'form-group--select-list',
'form-group--category',
Classes.FILL,
)}
>
<Select
items={accounts}
itemRenderer={accountItem}
itemPredicate={filterAccounts}
popoverProps={{ minimal: true }}
onItemSelect={onItemAccountSelect('category_id')}
>
<Button
fill={true}
rightIcon='caret-down'
text={getSelectedAccountLabel('category_id', 'Select account')}
/>
</Select>
</FormGroup>
</div>
<Row gutterWidth={16} className={'item-form__accounts-section'}>
<Col width={404}>
<h4 >Purchase Information</h4>
<Row gutterWidth={16}>
<Col md={4}>
<FormGroup
label={'Selling Price'}
className={'form-group--item-selling-price'}
intent={formik.errors.sell_price && Intent.DANGER}
helperText={formik.errors.sell_price && formik.errors.sell_price}
intent={(errors.selling_price && touched.selling_price) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="selling_price" />}
inline={true}
>
<InputGroup
medium={true}
intent={formik.errors.sell_price && Intent.DANGER}
intent={(errors.selling_price && touched.selling_price) && Intent.DANGER}
{...formik.getFieldProps('sell_price')}
/>
</FormGroup>
</Col>
<Col md={4}>
<FormGroup
label={'Cost Price'}
className={'form-group--item-cost-price'}
intent={formik.errors.cost_price && Intent.DANGER}
helperText={formik.errors.cost_price && formik.errors.cost_price}
inline={true}
>
<InputGroup
medium={true}
intent={formik.errors.cost_price && Intent.DANGER}
{...formik.getFieldProps('cost_price')}
/>
</FormGroup>
</Col>
</Row>
<Row gutterWidth={16}>
<Col md={4}>
<FormGroup
label={'Account'}
className={'form-group--item-sell_account_id'}
labelInfo={infoIcon}
inline={true}
intent={formik.errors.sell_account_id && Intent.DANGER}
helperText={
formik.errors.sell_account_id && formik.errors.sell_account_id
}
intent={(errors.sell_account_id && touched.sell_account_id) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="sell_account_id" />}
className={classNames(
'form-group--sell-account',
'form-group--select-list',
Classes.FILL)}
>
<Select
items={accounts}
@@ -209,23 +229,39 @@ const ItemForm = ({
<Button
fill={true}
rightIcon='caret-down'
text={getSelectedAccountLabel(
'sell_account_id',
'Select account'
)}
text={getSelectedAccountLabel('sell_account_id', 'Select account')}
/>
</Select>
</FormGroup>
</Col>
<Col md={4}>
<Col width={404}>
<h4>Sales Information</h4>
<FormGroup
label={'Cost Price'}
className={'form-group--item-cost-price'}
intent={(errors.cost_price && touched.cost_price) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="cost_price" />}
inline={true}
>
<InputGroup
medium={true}
intent={(errors.cost_price && touched.cost_price) && Intent.DANGER}
{...formik.getFieldProps('cost_price')}
/>
</FormGroup>
<FormGroup
label={'Account'}
className={'form-group--item-cost_account_id'}
labelInfo={infoIcon}
inline={true}
intent={formik.errors.cost_account_id && Intent.DANGER}
helperText={
formik.errors.cost_account_id && formik.errors.cost_account_id
}
intent={(errors.cost_account_id && touched.cost_account_id) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="cost_account_id" />}
className={classNames(
'form-group--cost-account',
'form-group--select-list',
Classes.FILL)}
>
<Select
items={accounts}
@@ -237,61 +273,65 @@ const ItemForm = ({
<Button
fill={true}
rightIcon='caret-down'
text={getSelectedAccountLabel(
'cost_account_id',
'Select account'
)}
text={getSelectedAccountLabel('cost_account_id', 'Select account')}
/>
</Select>
</FormGroup>
</Col>
</Row>
<FormGroup
label={'Inventory Account'}
className={'form-group--item-inventory_account_id'}
inline={true}
intent={formik.errors.inventory_account_id && Intent.DANGER}
helperText={
formik.errors.inventory_account_id &&
formik.errors.inventory_account_id
}
>
<Select
items={accounts}
itemRenderer={accountItem}
itemPredicate={filterAccounts}
popoverProps={{ minimal: true }}
onItemSelect={onItemAccountSelect('inventory_account_id')}
>
<Button
fill={true}
rightIcon='caret-down'
text={getSelectedAccountLabel(
'inventory_account_id',
'Select account'
)}
/>
</Select>
</FormGroup>
<FormGroup
label={'Opening Stock'}
className={'form-group--item-stock'}
intent={formik.errors.cost_price && Intent.DANGER}
helperText={formik.errors.stock && formik.errors.stock}
inline={true}
>
<InputGroup
medium={true}
intent={formik.errors.stock && Intent.DANGER}
{...formik.getFieldProps('stock')}
/>
</FormGroup>
<Row className={'item-form__accounts-section mt2'}>
<Col width={404}>
<h4>Inventory Information</h4>
<FormGroup
label={'Inventory Account'}
inline={true}
intent={(errors.inventory_account_id && errors.inventory_account_id) && Intent.DANGER}
helperText={<ErrorMessage {...formik} name="inventory_account_id" />}
className={classNames(
'form-group--item-inventory_account',
'form-group--select-list',
Classes.FILL)}
>
<Select
items={accounts}
itemRenderer={accountItem}
itemPredicate={filterAccounts}
popoverProps={{ minimal: true }}
onItemSelect={onItemAccountSelect('inventory_account_id')}
>
<Button
fill={true}
rightIcon='caret-down'
text={getSelectedAccountLabel('inventory_account_id','Select account')}
/>
</Select>
</FormGroup>
<FormGroup
label={'Opening Stock'}
className={'form-group--item-stock'}
intent={formik.errors.cost_price && Intent.DANGER}
helperText={formik.errors.stock && formik.errors.stock}
inline={true}
>
<InputGroup
medium={true}
intent={formik.errors.stock && Intent.DANGER}
{...formik.getFieldProps('stock')}
/>
</FormGroup>
</Col>
</Row>
<div class='form__floating-footer'>
<Button intent={Intent.PRIMARY} type='submit'>
Save
</Button>
<Button>Save as Draft</Button>
<Button onClick={'handleClose'}>Close</Button>
<Button className={'ml1'}>Save as Draft</Button>
<Button className={'ml1'} onClick={'handleClose'}>Close</Button>
</div>
</form>
</div>

View File

@@ -30,12 +30,6 @@ $pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
@import "components/data-table";
@import "components/dialog";
.bp3-breadcrumbs-collapsed::before{
background: escape-svg($breadcrumbs-collapsed-icon) center center;
width: 15px;
height: 15px;
}
// Pages
@import "pages/dashboard";
@import "pages/accounts-chart";
@@ -47,9 +41,8 @@ $pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
@import "pages/view-form";
@import "pages/manual-journals";
@import "pages/item-category";
@import "pages/items";
// Views
@import "views/filter-dropdown";
@import "views/sidebar";
@import "views/sidebar";

View File

@@ -92,6 +92,11 @@
&:hover{
background: $light-gray2;
}
&::before{
background: escape-svg($breadcrumbs-collapsed-icon) center center;
width: 15px;
height: 15px;
}
}
}

View File

@@ -0,0 +1,48 @@
.item-form{
padding: 22px;
padding-bottom: 90px;
&__primary-section{
background-color: #FAFAFA;
padding: 40px 22px 22px;
margin: -22px -22px 22px;
background-color: #FAFAFA;
}
&__accounts-section{
h4{
margin: 0;
font-weight: 500;
margin-bottom: 20px;
color: #828282;
}
> div:first-of-type{
padding-right: 15px !important;
}
> div ~ div{
padding-left: 15px !important;
border-left: 1px solid #e8e8e8;
}
}
.#{$ns}-form-group{
.#{$ns}-label{
width: 130px;
}
.#{$ns}-form-content{
width: 250px;
}
}
.form-group--item-type,
.form-group--item-name{
.#{$ns}-form-content{
width: 350px;
}
}
}