mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
refactor: invoice topbar.
This commit is contained in:
69
src/components/Branches/BranchSelect.js
Normal file
69
src/components/Branches/BranchSelect.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { MenuItem, Button } from '@blueprintjs/core';
|
||||||
|
import { FSelect } from '../Forms';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} query
|
||||||
|
* @param {*} branch
|
||||||
|
* @param {*} _index
|
||||||
|
* @param {*} exactMatch
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const branchItemPredicate = (query, branch, _index, exactMatch) => {
|
||||||
|
const normalizedTitle = branch.name.toLowerCase();
|
||||||
|
const normalizedQuery = query.toLowerCase();
|
||||||
|
|
||||||
|
if (exactMatch) {
|
||||||
|
return normalizedTitle === normalizedQuery;
|
||||||
|
} else {
|
||||||
|
return `${branch.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} film
|
||||||
|
* @param {*} param1
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const branchItemRenderer = (branch, { handleClick, modifiers, query }) => {
|
||||||
|
const text = `${branch.name}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
active={modifiers.active}
|
||||||
|
disabled={modifiers.disabled}
|
||||||
|
label={branch.name.toString()}
|
||||||
|
key={branch.id}
|
||||||
|
onClick={handleClick}
|
||||||
|
text={text}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const branchSelectProps = {
|
||||||
|
itemPredicate: branchItemPredicate,
|
||||||
|
itemRenderer: branchItemRenderer,
|
||||||
|
valueAccessor: 'id',
|
||||||
|
labelAccessor: 'name',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function BranchSelect({ branches, ...rest }) {
|
||||||
|
return <FSelect {...branchSelectProps} {...rest} items={branches} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function BranchSelectButton({ label, ...rest }) {
|
||||||
|
return <Button text={label} {...rest} />;
|
||||||
|
}
|
||||||
1
src/components/Branches/index.js
Normal file
1
src/components/Branches/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './BranchSelect';
|
||||||
85
src/components/Forms/FSelect.tsx
Normal file
85
src/components/Forms/FSelect.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Select as BPSelect,
|
||||||
|
SelectProps as BPSelectProps,
|
||||||
|
} from '@blueprintjs/select';
|
||||||
|
import { Field, FieldProps, FieldConfig, isFunction } from 'formik';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
|
||||||
|
interface SelectProps
|
||||||
|
extends Omit<FieldConfig, 'children' | 'as' | 'component'>,
|
||||||
|
BPSelectProps<any> {
|
||||||
|
valueAccessor: string;
|
||||||
|
labelAccessor: string;
|
||||||
|
input: () => JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FieldToSelectProps
|
||||||
|
extends Omit<BPSelectProps<any>, 'onItemSelect'>,
|
||||||
|
FieldProps {
|
||||||
|
onItemSelect?: (item: any, event?: React.SyntheticEvent<HTMLElement>) => void;
|
||||||
|
valueAccessor: string;
|
||||||
|
labelAccessor: string;
|
||||||
|
input: (props: { activeItem: any; label: any }) => JSX.Element;
|
||||||
|
children: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAccessor = (accessor: any, activeItem: any) => {
|
||||||
|
return isFunction(accessor)
|
||||||
|
? accessor(activeItem)
|
||||||
|
: get(activeItem, accessor);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform field props to select props.
|
||||||
|
* @param {FieldToSelectProps}
|
||||||
|
* @returns {BPSelectProps<any> }
|
||||||
|
*/
|
||||||
|
function transformSelectToFieldProps({
|
||||||
|
field: { onBlur: onFieldBlur, ...field },
|
||||||
|
form: { touched, errors, ...form },
|
||||||
|
meta,
|
||||||
|
input,
|
||||||
|
valueAccessor,
|
||||||
|
labelAccessor,
|
||||||
|
...props
|
||||||
|
}: FieldToSelectProps): BPSelectProps<any> {
|
||||||
|
const activeItem = props.items.find(
|
||||||
|
(item) => getAccessor(valueAccessor, item) === field.value
|
||||||
|
);
|
||||||
|
const children = input
|
||||||
|
? input({
|
||||||
|
activeItem,
|
||||||
|
label: getAccessor(labelAccessor, activeItem),
|
||||||
|
})
|
||||||
|
: props.children;
|
||||||
|
|
||||||
|
return {
|
||||||
|
onItemSelect: (item) => {
|
||||||
|
const value = getAccessor(valueAccessor, item);
|
||||||
|
form.setFieldValue(field.name, value);
|
||||||
|
},
|
||||||
|
activeItem,
|
||||||
|
...props,
|
||||||
|
children,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {FieldToSelectProps}
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
function FieldToSelect({ ...props }: FieldToSelectProps): JSX.Element {
|
||||||
|
return <BPSelect {...transformSelectToFieldProps(props)} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select binded with formik.
|
||||||
|
* @param {JSX.Element}
|
||||||
|
* @returns {SelectProps}
|
||||||
|
*/
|
||||||
|
export function FSelect({ ...props }: SelectProps): JSX.Element {
|
||||||
|
return <Field {...props} component={FieldToSelect} />;
|
||||||
|
}
|
||||||
@@ -2,3 +2,4 @@ export * from './FormObserver';
|
|||||||
export * from './FormikObserver';
|
export * from './FormikObserver';
|
||||||
export * from './FMoneyInputGroup'
|
export * from './FMoneyInputGroup'
|
||||||
export * from './FFormGroup'
|
export * from './FFormGroup'
|
||||||
|
export * from './FSelect'
|
||||||
74
src/components/Warehouses/WarehouseSelect.js
Normal file
74
src/components/Warehouses/WarehouseSelect.js
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { MenuItem, Button } from '@blueprintjs/core';
|
||||||
|
import { FSelect } from '../Forms';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} query
|
||||||
|
* @param {*} warehouse
|
||||||
|
* @param {*} _index
|
||||||
|
* @param {*} exactMatch
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const warehouseItemPredicate = (query, warehouse, _index, exactMatch) => {
|
||||||
|
const normalizedTitle = warehouse.name.toLowerCase();
|
||||||
|
const normalizedQuery = query.toLowerCase();
|
||||||
|
|
||||||
|
if (exactMatch) {
|
||||||
|
return normalizedTitle === normalizedQuery;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
`${warehouse.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} film
|
||||||
|
* @param {*} param1
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const warehouseItemRenderer = (
|
||||||
|
warehouse,
|
||||||
|
{ handleClick, modifiers, query },
|
||||||
|
) => {
|
||||||
|
const text = `${warehouse.name}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
active={modifiers.active}
|
||||||
|
disabled={modifiers.disabled}
|
||||||
|
label={warehouse.name.toString()}
|
||||||
|
key={warehouse.id}
|
||||||
|
onClick={handleClick}
|
||||||
|
text={text}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const warehouseSelectProps = {
|
||||||
|
itemPredicate: warehouseItemPredicate,
|
||||||
|
itemRenderer: warehouseItemRenderer,
|
||||||
|
valueAccessor: 'id',
|
||||||
|
labelAccessor: 'name',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function WarehouseSelect({ warehouses, ...rest }) {
|
||||||
|
return <FSelect {...warehouseSelectProps} {...rest} items={warehouses} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} param0
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function WarehouseSelectButton({ label, ...rest }) {
|
||||||
|
return <Button text={label} />;
|
||||||
|
}
|
||||||
1
src/components/Warehouses/index.js
Normal file
1
src/components/Warehouses/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './WarehouseSelect';
|
||||||
@@ -98,7 +98,10 @@ export * from './FinancialStatement';
|
|||||||
export * from './FinancialReport';
|
export * from './FinancialReport';
|
||||||
export * from './FinancialSheet';
|
export * from './FinancialSheet';
|
||||||
export * from './FeatureGuard';
|
export * from './FeatureGuard';
|
||||||
export * from './ExchangeRate'
|
export * from './ExchangeRate';
|
||||||
|
export * from './Branches';
|
||||||
|
export * from './Warehouses';
|
||||||
|
|
||||||
const Hint = FieldHint;
|
const Hint = FieldHint;
|
||||||
|
|
||||||
const T = FormattedMessage;
|
const T = FormattedMessage;
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FastField, Field } from 'formik';
|
|
||||||
import {
|
import {
|
||||||
Alignment,
|
Alignment,
|
||||||
Navbar,
|
Navbar,
|
||||||
NavbarGroup,
|
NavbarGroup,
|
||||||
NavbarDivider,
|
NavbarDivider,
|
||||||
|
Button,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
|
import intl from 'react-intl-universal';
|
||||||
|
|
||||||
import { useFeatureCan } from 'hooks/state';
|
import { useFeatureCan } from 'hooks/state';
|
||||||
import {
|
import { Icon, BranchSelect, FeatureCan, WarehouseSelect } from 'components';
|
||||||
Icon,
|
|
||||||
FormattedMessage as T,
|
|
||||||
CustomSelectList,
|
|
||||||
FeatureCan,
|
|
||||||
} from 'components';
|
|
||||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||||
import { Features } from 'common';
|
import { Features } from 'common';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice form topbar .
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
export default function InvoiceFormTopBar() {
|
export default function InvoiceFormTopBar() {
|
||||||
const { branches, warehouses, isWarehouesLoading, isBranchesLoading } =
|
// Invoice form context.
|
||||||
useInvoiceFormContext();
|
const { branches, warehouses } = useInvoiceFormContext();
|
||||||
|
|
||||||
|
// Features guard.
|
||||||
const { featureCan } = useFeatureCan();
|
const { featureCan } = useFeatureCan();
|
||||||
|
|
||||||
// Can't display the navigation bar if warehouses or branches feature is not enabled.
|
// Can't display the navigation bar if warehouses or branches feature is not enabled.
|
||||||
@@ -31,46 +32,52 @@ export default function InvoiceFormTopBar() {
|
|||||||
<Navbar className={'navbar--dashboard-topbar'}>
|
<Navbar className={'navbar--dashboard-topbar'}>
|
||||||
<NavbarGroup align={Alignment.LEFT}>
|
<NavbarGroup align={Alignment.LEFT}>
|
||||||
<FeatureCan feature={Features.Warehouses}>
|
<FeatureCan feature={Features.Warehouses}>
|
||||||
<Field name={'branch_id'}>
|
<BranchSelect
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'branch_id'}
|
||||||
<CustomSelectList
|
branches={branches}
|
||||||
items={branches}
|
input={InvoiceBranchSelectButton}
|
||||||
loading={isBranchesLoading}
|
popoverProps={{
|
||||||
defaultSelectText={'Branch'}
|
minimal: true,
|
||||||
onItemSelected={({ id }) => {
|
|
||||||
form.setFieldValue('branch_id', id);
|
|
||||||
}}
|
|
||||||
selectedItemId={value}
|
|
||||||
buttonProps={{
|
|
||||||
icon: <Icon icon={'branch-16'} iconSize={16} />,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FeatureCan>
|
</FeatureCan>
|
||||||
|
|
||||||
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
|
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
)}
|
)}
|
||||||
<FeatureCan feature={Features.Warehouses}>
|
<FeatureCan feature={Features.Warehouses}>
|
||||||
<Field name={'warehouse_id'}>
|
<WarehouseSelect
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'warehouse_id'}
|
||||||
<CustomSelectList
|
warehouses={warehouses}
|
||||||
items={warehouses}
|
input={InvoiceWarehouseSelectButton}
|
||||||
loading={isWarehouesLoading}
|
popoverProps={{
|
||||||
defaultSelectText={'Warehosue'}
|
minimal: true,
|
||||||
onItemSelected={({ id }) => {
|
|
||||||
form.setFieldValue('warehouse_id', id);
|
|
||||||
}}
|
|
||||||
selectedItemId={value}
|
|
||||||
buttonProps={{
|
|
||||||
icon: <Icon icon={'warehouse-16'} iconSize={16} />,
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</Field>
|
|
||||||
</FeatureCan>
|
</FeatureCan>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function InvoiceWarehouseSelectButton({ label }) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
text={intl.get('invoice.branch_button.label', { label })}
|
||||||
|
minimal={true}
|
||||||
|
small={true}
|
||||||
|
icon={<Icon icon={'warehouse-16'} iconSize={16} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function InvoiceBranchSelectButton({ label }) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
text={intl.get('invoice.warehouse_button.label', { label })}
|
||||||
|
minimal={true}
|
||||||
|
small={true}
|
||||||
|
icon={<Icon icon={'branch-16'} iconSize={16} />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1851,5 +1851,7 @@
|
|||||||
"warehouse_activate.dialog_success_message": "Multi-branches feature has been activated successfully.",
|
"warehouse_activate.dialog_success_message": "Multi-branches feature has been activated successfully.",
|
||||||
"warehouse.alert.mark_primary_message": "The given warehouse has been marked as primary.",
|
"warehouse.alert.mark_primary_message": "The given warehouse has been marked as primary.",
|
||||||
"warehouse.alert.are_you_sure_you_want_to_make": "Are you sure you want to make a primary warehouse?",
|
"warehouse.alert.are_you_sure_you_want_to_make": "Are you sure you want to make a primary warehouse?",
|
||||||
"make_primary": "Make as Primary"
|
"make_primary": "Make as Primary",
|
||||||
|
"invoice.branch_button.label": "Branch: {label}",
|
||||||
|
"invoice.warehouse_button.label": "Warehouse: {label}"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user