mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
882fc20ac1 |
@@ -3,7 +3,15 @@ import React from 'react';
|
|||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
import { Navbar } from '@blueprintjs/core';
|
import { Navbar } from '@blueprintjs/core';
|
||||||
|
|
||||||
export function DashboardActionsBar({ className, children, name }) {
|
interface DashboardActionsBarProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DashboardActionsBar({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
name,
|
||||||
|
}: DashboardActionsBarProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@@ -20,8 +20,9 @@ export const AbilitySubject = {
|
|||||||
SubscriptionBilling: 'SubscriptionBilling',
|
SubscriptionBilling: 'SubscriptionBilling',
|
||||||
CreditNote: 'CreditNote',
|
CreditNote: 'CreditNote',
|
||||||
VendorCredit: 'VendorCredit',
|
VendorCredit: 'VendorCredit',
|
||||||
Project:'Project',
|
Project: 'Project',
|
||||||
TaxRate: 'TaxRate',
|
TaxRate: 'TaxRate',
|
||||||
|
BankRule: 'BankRule',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ItemAction = {
|
export const ItemAction = {
|
||||||
@@ -188,10 +189,16 @@ export const SubscriptionBillingAbility = {
|
|||||||
Payment: 'payment',
|
Payment: 'payment',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const TaxRateAction = {
|
export const TaxRateAction = {
|
||||||
View: 'View',
|
View: 'View',
|
||||||
Create: 'Create',
|
Create: 'Create',
|
||||||
Edit: 'Edit',
|
Edit: 'Edit',
|
||||||
Delete: 'Delete',
|
Delete: 'Delete',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const BankRuleAction = {
|
||||||
|
View: 'View',
|
||||||
|
Create: 'Create',
|
||||||
|
Edit: 'Edit',
|
||||||
|
Delete: 'Delete',
|
||||||
|
};
|
||||||
|
|||||||
@@ -458,6 +458,11 @@ export const SidebarMenu = [
|
|||||||
ability: CashflowAction.View,
|
ability: CashflowAction.View,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: 'Rules',
|
||||||
|
href: '/bank-rules',
|
||||||
|
type: ISidebarMenuItemType.Link,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import React, { createContext } from 'react';
|
||||||
|
import { DialogContent } from '@/components';
|
||||||
|
|
||||||
|
interface RuleFormBootValues {
|
||||||
|
bankRule?: null;
|
||||||
|
bankRuleId?: null;
|
||||||
|
isBankRuleLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RuleFormBootContext = createContext<RuleFormBootValues>(
|
||||||
|
{} as RuleFormBootValues,
|
||||||
|
);
|
||||||
|
|
||||||
|
interface RuleFormBootProps {
|
||||||
|
bankRuleId?: number;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RuleFormBoot({ bankRuleId, ...props }: RuleFormBootProps) {
|
||||||
|
const provider = {} as RuleFormBootValues;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogContent isLoading={false}>
|
||||||
|
<RuleFormBootContext.Provider value={provider} {...props} />
|
||||||
|
</DialogContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useRuleFormDialogBoot = () =>
|
||||||
|
React.useContext<RuleFormBootValues>(RuleFormBootContext);
|
||||||
|
|
||||||
|
export { RuleFormBoot, useRuleFormDialogBoot };
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { RuleFormBoot } from "./RuleFormBoot";
|
||||||
|
|
||||||
|
|
||||||
|
interface RuleFormContentProps {
|
||||||
|
dialogName: string;
|
||||||
|
bankRuleId?: number;
|
||||||
|
}
|
||||||
|
export function RuleFormContent({
|
||||||
|
dialogName,
|
||||||
|
bankRuleId,
|
||||||
|
}: RuleFormContentProps) {
|
||||||
|
return (
|
||||||
|
<RuleFormBoot
|
||||||
|
bankRuleId={bankRuleId}
|
||||||
|
>
|
||||||
|
|
||||||
|
</RuleFormBoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
|
const Schema = Yup.object().shape({
|
||||||
|
name: Yup.string().required().label('Rule name'),
|
||||||
|
applyIfAccountId: Yup.number().required().label(''),
|
||||||
|
applyIfTransactionType: Yup.string().required().label(''),
|
||||||
|
conditionsType: Yup.string().required(),
|
||||||
|
assignCategory: Yup.string().required(),
|
||||||
|
assignAccountId: Yup.string().required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateRuleFormSchema = Schema;
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import { Form, Formik, useFormikContext } from 'formik';
|
||||||
|
import { Button, Radio } from '@blueprintjs/core';
|
||||||
|
import { CreateRuleFormSchema } from './RuleFormContentForm.schema';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
FFormGroup,
|
||||||
|
FInputGroup,
|
||||||
|
FRadioGroup,
|
||||||
|
FSelect,
|
||||||
|
Group,
|
||||||
|
} from '@/components';
|
||||||
|
|
||||||
|
const initialValues = {
|
||||||
|
name: '',
|
||||||
|
order: 0,
|
||||||
|
applyIfAccountId: '',
|
||||||
|
applyIfTransactionType: '',
|
||||||
|
conditionsType: '',
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
field: 'description',
|
||||||
|
comparator: 'contains',
|
||||||
|
value: 'payment',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
assignCategory: '',
|
||||||
|
assignAccountId: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RuleFormValues {
|
||||||
|
name: string;
|
||||||
|
order: number;
|
||||||
|
applyIfAccountId: string;
|
||||||
|
applyIfTransactionType: string;
|
||||||
|
conditionsType: string;
|
||||||
|
conditions: Array<{
|
||||||
|
field: string;
|
||||||
|
comparator: string;
|
||||||
|
value: string;
|
||||||
|
}>;
|
||||||
|
assignCategory: string;
|
||||||
|
assignAccountId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RuleFormContentForm() {
|
||||||
|
const validationSchema = CreateRuleFormSchema;
|
||||||
|
const handleSubmit = () => {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Formik<RuleFormValues>
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<FFormGroup name={'name'} label={'Rule Name'}>
|
||||||
|
<FInputGroup name={'name'} />
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<FFormGroup name={'conditionsType'} label={'Apply to transactions are'}>
|
||||||
|
<FSelect name={'conditionsType'} items={[]} />
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<FFormGroup name={''} label={'Categorize the transactions when'}>
|
||||||
|
<FRadioGroup name={'conditionsType'}>
|
||||||
|
<Radio value={'and'} label={'All the following criteria matches'} />
|
||||||
|
<Radio
|
||||||
|
value={'or'}
|
||||||
|
label={'Any one of the following criteria matches'}
|
||||||
|
/>
|
||||||
|
</FRadioGroup>
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<RuleFormConditions />
|
||||||
|
|
||||||
|
<h3>Then Assign</h3>
|
||||||
|
|
||||||
|
<FFormGroup name={'assignCategory'} label={'Transaction type'}>
|
||||||
|
<FSelect name={'assignCategory'} items={[]} />
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<FFormGroup name={'assignAccountId'} label={'Account category'}>
|
||||||
|
<FSelect name={'assignAccountId'} items={[]} />
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<FFormGroup name={'assignRef'} label={'Reference'}>
|
||||||
|
<FInputGroup name={'assignRef'} />
|
||||||
|
</FFormGroup>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RuleFormConditions() {
|
||||||
|
const { values } = useFormikContext<RuleFormValues>();
|
||||||
|
|
||||||
|
const handleAddConditionBtnClick = () => {
|
||||||
|
values.conditions.push({
|
||||||
|
field: '',
|
||||||
|
comparator: '',
|
||||||
|
value: '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{values?.conditions?.map((condition, index) => (
|
||||||
|
<Group>
|
||||||
|
<FFormGroup name={`conditions[${index}].field`} label={'Field'}>
|
||||||
|
<FSelect name={`conditions[${index}].field`} items={[]} />
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<FFormGroup
|
||||||
|
name={`conditions[${index}].comparator`}
|
||||||
|
label={'Condition'}
|
||||||
|
>
|
||||||
|
<FSelect name={`conditions[${index}].comparator`} items={[]} />
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
|
<FFormGroup
|
||||||
|
name={`conditions[${index}].condition`}
|
||||||
|
label={'Condition'}
|
||||||
|
>
|
||||||
|
<FInputGroup name={`conditions[${index}].value`} />
|
||||||
|
</FFormGroup>
|
||||||
|
</Group>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Button type={'button'} onClick={handleAddConditionBtnClick}>
|
||||||
|
Add Condition
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import { Dialog, DialogSuspense } from '@/components';
|
||||||
|
import withDialogRedux from '@/components/DialogReduxConnect';
|
||||||
|
import { compose } from '@/utils';
|
||||||
|
|
||||||
|
const RuleFormContent = React.lazy(() => import('./RuleFormContent'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payment mail dialog.
|
||||||
|
*/
|
||||||
|
function RuleFormDialog({
|
||||||
|
dialogName,
|
||||||
|
payload: { bankRuleId = null },
|
||||||
|
isOpen,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
name={dialogName}
|
||||||
|
title={'New Bank Rule'}
|
||||||
|
isOpen={isOpen}
|
||||||
|
canEscapeJeyClose={true}
|
||||||
|
autoFocus={true}
|
||||||
|
style={{ width: 600 }}
|
||||||
|
>
|
||||||
|
<DialogSuspense>
|
||||||
|
<RuleFormContent dialogName={dialogName} bankRuleId={bankRuleId} />
|
||||||
|
</DialogSuspense>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(withDialogRedux())(RuleFormDialog);
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import { Button, Intent } from '@blueprintjs/core';
|
||||||
|
import { EmptyStatus, Can, FormattedMessage as T } from '@/components';
|
||||||
|
import { AbilitySubject, BankRuleAction } from '@/constants/abilityOption';
|
||||||
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
|
|
||||||
|
function BankRulesLandingEmptyStateRoot({
|
||||||
|
// #withDialogAction
|
||||||
|
openDialog,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<EmptyStatus
|
||||||
|
title={"The organization doesn't have taxes, yet!"}
|
||||||
|
description={
|
||||||
|
<p>
|
||||||
|
Setup the organization taxes to start tracking taxes on sales
|
||||||
|
transactions.
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
action={
|
||||||
|
<>
|
||||||
|
<Can I={BankRuleAction.Create} a={AbilitySubject.BankRule}>
|
||||||
|
<Button intent={Intent.PRIMARY} large={true} onClick={() => {}}>
|
||||||
|
New tax rate
|
||||||
|
</Button>
|
||||||
|
<Button intent={Intent.NONE} large={true}>
|
||||||
|
<T id={'learn_more'} />
|
||||||
|
</Button>
|
||||||
|
</Can>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BankRulesLandingEmptyState = R.compose(withDialogActions)(
|
||||||
|
BankRulesLandingEmptyStateRoot,
|
||||||
|
);
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import { DashboardPageContent } from '@/components';
|
||||||
|
import { RulesListBoot } from './RulesListBoot';
|
||||||
|
import { RulesListActionsBar } from './RulesListActionsBar';
|
||||||
|
import { BankRulesTable } from './RulesTable';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function RulesList() {
|
||||||
|
return (
|
||||||
|
<RulesListBoot>
|
||||||
|
<RulesListActionsBar />
|
||||||
|
|
||||||
|
<DashboardPageContent>
|
||||||
|
<BankRulesTable />
|
||||||
|
</DashboardPageContent>
|
||||||
|
</RulesListBoot>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { DashboardActionsBar } from '@/components';
|
||||||
|
import { NavbarGroup } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
export function RulesListActionsBar() {
|
||||||
|
return (
|
||||||
|
<DashboardActionsBar>
|
||||||
|
<NavbarGroup></NavbarGroup>
|
||||||
|
</DashboardActionsBar>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import React, { createContext } from 'react';
|
||||||
|
import { DialogContent } from '@/components';
|
||||||
|
|
||||||
|
interface RulesListBootValues {
|
||||||
|
rules: any;
|
||||||
|
isRulesLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RulesListBootContext = createContext<RulesListBootValues>(
|
||||||
|
{} as RulesListBootValues,
|
||||||
|
);
|
||||||
|
|
||||||
|
interface RulesListBootProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RulesListBoot({ ...props }: RulesListBootProps) {
|
||||||
|
const provider = {} as RulesListBootValues;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogContent isLoading={false}>
|
||||||
|
<RulesListBootContext.Provider value={provider} {...props} />
|
||||||
|
</DialogContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useRulesListBoot = () =>
|
||||||
|
React.useContext<RulesListBootValues>(RulesListBootContext);
|
||||||
|
|
||||||
|
export { RulesListBoot, useRulesListBoot };
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import {
|
||||||
|
DataTable,
|
||||||
|
DashboardContentTable,
|
||||||
|
TableSkeletonHeader,
|
||||||
|
TableSkeletonRows,
|
||||||
|
} from '@/components';
|
||||||
|
|
||||||
|
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||||
|
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||||
|
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||||
|
import withDashboardActions from '@/containers/Dashboard/withDashboardActions';
|
||||||
|
|
||||||
|
import { useBankRulesTableColumns } from './hooks';
|
||||||
|
import { BankRulesTableActionsMenu } from './_components';
|
||||||
|
import { BankRulesLandingEmptyState } from './BankRulesLandingEmptyState';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoices datatable.
|
||||||
|
*/
|
||||||
|
function RulesTable({
|
||||||
|
// #withAlertsActions
|
||||||
|
openAlert,
|
||||||
|
|
||||||
|
// #withDrawerActions
|
||||||
|
openDrawer,
|
||||||
|
|
||||||
|
// #withDialogAction
|
||||||
|
openDialog,
|
||||||
|
}) {
|
||||||
|
// Invoices table columns.
|
||||||
|
const columns = useBankRulesTableColumns();
|
||||||
|
|
||||||
|
// Handle edit bank rule.
|
||||||
|
const handleDeleteBankRule = ({ id }) => {};
|
||||||
|
|
||||||
|
// Handle delete bank rule.
|
||||||
|
const handleEditBankRule = () => {};
|
||||||
|
|
||||||
|
// Display invoice empty status instead of the table.
|
||||||
|
if (isEmptyStatus) {
|
||||||
|
return <BankRulesLandingEmptyState />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardContentTable>
|
||||||
|
<DataTable
|
||||||
|
columns={columns}
|
||||||
|
data={[]}
|
||||||
|
loading={false}
|
||||||
|
headerLoading={false}
|
||||||
|
progressBarLoading={false}
|
||||||
|
manualSortBy={false}
|
||||||
|
selectionColumn={false}
|
||||||
|
noInitialFetch={true}
|
||||||
|
sticky={true}
|
||||||
|
pagination={false}
|
||||||
|
manualPagination={false}
|
||||||
|
autoResetSortBy={false}
|
||||||
|
autoResetPage={false}
|
||||||
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
|
ContextMenu={BankRulesTableActionsMenu}
|
||||||
|
onCellClick={handleCellClick}
|
||||||
|
size={'medium'}
|
||||||
|
payload={{
|
||||||
|
onDelete: handleDeleteTaxRate,
|
||||||
|
onEdit: handleEditTaxRate,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DashboardContentTable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BankRulesTable = R.compose(
|
||||||
|
withDashboardActions,
|
||||||
|
withAlertsActions,
|
||||||
|
withDrawerActions,
|
||||||
|
withDialogActions,
|
||||||
|
)(RulesTable);
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import { Intent, Menu, MenuDivider, MenuItem } from '@blueprintjs/core';
|
||||||
|
import { Can, Icon } from '@/components';
|
||||||
|
import { AbilitySubject, BankRuleAction } from '@/constants/abilityOption';
|
||||||
|
import { safeCallback } from '@/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tax rates table actions menu.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
export function BankRulesTableActionsMenu({
|
||||||
|
payload: { onEdit, onDelete },
|
||||||
|
row: { original },
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Menu>
|
||||||
|
<Can I={BankRuleAction.Edit} a={AbilitySubject.BankRule}>
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuItem
|
||||||
|
icon={<Icon icon="pen-18" />}
|
||||||
|
text={'Edit Rule'}
|
||||||
|
onClick={safeCallback(onEdit, original)}
|
||||||
|
/>
|
||||||
|
</Can>
|
||||||
|
<MenuDivider />
|
||||||
|
<Can I={BankRuleAction.Delete} a={AbilitySubject.BankRule}>
|
||||||
|
<MenuDivider />
|
||||||
|
<MenuItem
|
||||||
|
text={'Delete Rule'}
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
onClick={safeCallback(onDelete, original)}
|
||||||
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
|
/>
|
||||||
|
</Can>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const useBankRulesTableColumns = () => {
|
||||||
|
return [];
|
||||||
|
};
|
||||||
@@ -1221,6 +1221,14 @@ export const getDashboardRoutes = () => [
|
|||||||
pageTitle: 'Tax Rates',
|
pageTitle: 'Tax Rates',
|
||||||
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
|
||||||
},
|
},
|
||||||
|
// Bank Rules
|
||||||
|
{
|
||||||
|
path: '/bank-rules',
|
||||||
|
component: lazy(
|
||||||
|
() => import('@/containers/Banking/Rules/RulesList/RulesList'),
|
||||||
|
),
|
||||||
|
pageTitle: 'Bank Rules',
|
||||||
|
},
|
||||||
// Homepage
|
// Homepage
|
||||||
{
|
{
|
||||||
path: `/`,
|
path: `/`,
|
||||||
|
|||||||
Reference in New Issue
Block a user