mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 05:40:31 +00:00
chrone: sperate client and server to different repos.
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import React from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import {
|
||||
Button,
|
||||
NavbarGroup,
|
||||
Classes,
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
Alignment,
|
||||
} from '@blueprintjs/core';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
AdvancedFilterPopover,
|
||||
DashboardFilterButton,
|
||||
FormattedMessage as T,
|
||||
} from 'components';
|
||||
|
||||
import { useRefreshJournals } from 'hooks/query/manualJournals';
|
||||
import { useManualJournalsContext } from './ManualJournalsListProvider';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withManualJournalsActions from './withManualJournalsActions';
|
||||
import withManualJournals from './withManualJournals';
|
||||
|
||||
import { If, DashboardActionViewsList } from 'components';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Manual journal actions bar.
|
||||
*/
|
||||
function ManualJournalActionsBar({
|
||||
// #withManualJournalsActions
|
||||
setManualJournalsTableState,
|
||||
|
||||
// #withManualJournals
|
||||
manualJournalsFilterConditions,
|
||||
}) {
|
||||
// History context.
|
||||
const history = useHistory();
|
||||
|
||||
// Manual journals context.
|
||||
const { journalsViews, fields } = useManualJournalsContext();
|
||||
|
||||
// Manual journals refresh action.
|
||||
const { refresh } = useRefreshJournals();
|
||||
|
||||
// Handle click a new manual journal.
|
||||
const onClickNewManualJournal = () => {
|
||||
history.push('/make-journal-entry');
|
||||
};
|
||||
// Handle delete button click.
|
||||
const handleBulkDelete = () => {};
|
||||
|
||||
// Handle tab change.
|
||||
const handleTabChange = (view) => {
|
||||
setManualJournalsTableState({ viewSlug: view ? view.slig : null });
|
||||
};
|
||||
// Handle click a refresh Journals
|
||||
const handleRefreshBtnClick = () => {
|
||||
refresh();
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<DashboardActionViewsList
|
||||
resourceName={'manual-journals'}
|
||||
allMenuItem={true}
|
||||
views={journalsViews}
|
||||
onChange={handleTabChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="plus" />}
|
||||
text={<T id={'journal_entry'} />}
|
||||
onClick={onClickNewManualJournal}
|
||||
/>
|
||||
<AdvancedFilterPopover
|
||||
advancedFilterProps={{
|
||||
conditions: manualJournalsFilterConditions,
|
||||
defaultFieldKey: 'journal_number',
|
||||
fields,
|
||||
onFilterChange: (filterConditions) => {
|
||||
setManualJournalsTableState({ filterRoles: filterConditions });
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DashboardFilterButton
|
||||
conditionsCount={manualJournalsFilterConditions.length}
|
||||
/>
|
||||
</AdvancedFilterPopover>
|
||||
|
||||
<If condition={false}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleBulkDelete}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||
text={<T id={'import'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align={Alignment.RIGHT}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="refresh-16" iconSize={14} />}
|
||||
onClick={handleRefreshBtnClick}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withManualJournalsActions,
|
||||
withManualJournals(({ manualJournalsTableState }) => ({
|
||||
manualJournalsFilterConditions: manualJournalsTableState.filterRoles,
|
||||
})),
|
||||
)(ManualJournalActionsBar);
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import JournalDeleteAlert from 'containers/Alerts/ManualJournals/JournalDeleteAlert';
|
||||
import JournalPublishAlert from 'containers/Alerts/ManualJournals/JournalPublishAlert';
|
||||
|
||||
/**
|
||||
* Manual journals alerts.
|
||||
*/
|
||||
export default function ManualJournalsAlerts() {
|
||||
return (
|
||||
<div>
|
||||
<JournalDeleteAlert name={'journal-delete'} />
|
||||
<JournalPublishAlert name={'journal-publish'} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { DataTable, DashboardContentTable } from 'components';
|
||||
import { TABLES } from 'common/tables';
|
||||
|
||||
import ManualJournalsEmptyStatus from './ManualJournalsEmptyStatus';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
||||
import { ActionsMenu } from './components';
|
||||
|
||||
import withManualJournals from './withManualJournals';
|
||||
import withManualJournalsActions from './withManualJournalsActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { useManualJournalsContext } from './ManualJournalsListProvider';
|
||||
import { useMemorizedColumnsWidths } from 'hooks';
|
||||
import { useManualJournalsColumns } from './utils';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Manual journals data-table.
|
||||
*/
|
||||
function ManualJournalsDataTable({
|
||||
// #withManualJournalsActions
|
||||
setManualJournalsTableState,
|
||||
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
|
||||
// #withManualJournals
|
||||
manualJournalsTableState,
|
||||
|
||||
// #ownProps
|
||||
onSelectedRowsChange,
|
||||
}) {
|
||||
// Manual journals context.
|
||||
const {
|
||||
manualJournals,
|
||||
pagination,
|
||||
isManualJournalsLoading,
|
||||
isManualJournalsFetching,
|
||||
isEmptyStatus,
|
||||
} = useManualJournalsContext();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
// Manual journals columns.
|
||||
const columns = useManualJournalsColumns();
|
||||
|
||||
// Handles the journal publish action.
|
||||
const handlePublishJournal = ({ id }) => {
|
||||
openAlert('journal-publish', { manualJournalId: id });
|
||||
};
|
||||
|
||||
// Handle the journal edit action.
|
||||
const handleEditJournal = ({ id }) => {
|
||||
history.push(`/manual-journals/${id}/edit`);
|
||||
};
|
||||
|
||||
// Handle the journal delete action.
|
||||
const handleDeleteJournal = ({ id }) => {
|
||||
openAlert('journal-delete', { manualJournalId: id });
|
||||
};
|
||||
|
||||
// Handle view detail journal.
|
||||
const handleViewDetailJournal = ({ id }) => {
|
||||
openDrawer('journal-drawer', {
|
||||
manualJournalId: id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle cell click.
|
||||
const handleCellClick = (cell, event) => {
|
||||
openDrawer('journal-drawer', { manualJournalId: cell.row.original.id });
|
||||
};
|
||||
|
||||
// Local storage memorizing columns widths.
|
||||
const [initialColumnsWidths, , handleColumnResizing] =
|
||||
useMemorizedColumnsWidths(TABLES.MANUAL_JOURNALS);
|
||||
|
||||
// Handle fetch data once the page index, size or sort by of the table change.
|
||||
const handleFetchData = React.useCallback(
|
||||
({ pageSize, pageIndex, sortBy }) => {
|
||||
setManualJournalsTableState({
|
||||
pageIndex,
|
||||
pageSize,
|
||||
sortBy,
|
||||
});
|
||||
},
|
||||
[setManualJournalsTableState],
|
||||
);
|
||||
|
||||
// Display manual journal empty status instead of the table.
|
||||
if (isEmptyStatus) {
|
||||
return <ManualJournalsEmptyStatus />;
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardContentTable>
|
||||
<DataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={manualJournals}
|
||||
manualSortBy={true}
|
||||
selectionColumn={true}
|
||||
expandable={true}
|
||||
sticky={true}
|
||||
loading={isManualJournalsLoading}
|
||||
headerLoading={isManualJournalsLoading}
|
||||
progressBarLoading={isManualJournalsFetching}
|
||||
pagesCount={pagination.pagesCount}
|
||||
pagination={true}
|
||||
autoResetSortBy={false}
|
||||
autoResetPage={false}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||
ContextMenu={ActionsMenu}
|
||||
onFetchData={handleFetchData}
|
||||
onCellClick={handleCellClick}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
payload={{
|
||||
onDelete: handleDeleteJournal,
|
||||
onPublish: handlePublishJournal,
|
||||
onEdit: handleEditJournal,
|
||||
onViewDetails: handleViewDetailJournal,
|
||||
}}
|
||||
/>
|
||||
</DashboardContentTable>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withManualJournalsActions,
|
||||
withManualJournals(({ manualJournalsTableState }) => ({
|
||||
manualJournalsTableState,
|
||||
})),
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(ManualJournalsDataTable);
|
||||
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { Button, Intent } from '@blueprintjs/core';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { EmptyStatus } from 'components';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
export default function ManualJournalsEmptyStatus() {
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<EmptyStatus
|
||||
title={<T id={'manual_journals.empty_status.title'} />}
|
||||
description={
|
||||
<p>
|
||||
<T id={'manual_journals.empty_status.description'} />
|
||||
</p>
|
||||
}
|
||||
action={
|
||||
<>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
large={true}
|
||||
onClick={() => {
|
||||
history.push('/make-journal-entry');
|
||||
}}
|
||||
>
|
||||
<T id={'make_journal'} />
|
||||
</Button>
|
||||
|
||||
<Button intent={Intent.NONE} large={true}>
|
||||
<T id={'learn_more'} />
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/pages/ManualJournal/List.scss';
|
||||
|
||||
import { DashboardContentTable, DashboardPageContent } from 'components';
|
||||
|
||||
import { ManualJournalsListProvider } from './ManualJournalsListProvider';
|
||||
import ManualJournalsAlerts from './ManualJournalsAlerts';
|
||||
import ManualJournalsViewTabs from './ManualJournalsViewTabs';
|
||||
import ManualJournalsDataTable from './ManualJournalsDataTable';
|
||||
import ManualJournalsActionsBar from './ManualJournalActionsBar';
|
||||
|
||||
import withManualJournals from './withManualJournals';
|
||||
import { transformTableStateToQuery, compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Manual journals table.
|
||||
*/
|
||||
function ManualJournalsTable({
|
||||
// #withManualJournals
|
||||
journalsTableState,
|
||||
journalsTableStateChanged,
|
||||
}) {
|
||||
return (
|
||||
<ManualJournalsListProvider
|
||||
query={transformTableStateToQuery(journalsTableState)}
|
||||
tableStateChanged={journalsTableStateChanged}
|
||||
>
|
||||
<ManualJournalsActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<ManualJournalsViewTabs />
|
||||
<ManualJournalsDataTable />
|
||||
</DashboardPageContent>
|
||||
|
||||
<ManualJournalsAlerts />
|
||||
</ManualJournalsListProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withManualJournals(
|
||||
({ manualJournalsTableState, manualJournalTableStateChanged }) => ({
|
||||
journalsTableState: manualJournalsTableState,
|
||||
journalsTableStateChanged: manualJournalTableStateChanged,
|
||||
}),
|
||||
),
|
||||
)(ManualJournalsTable);
|
||||
@@ -0,0 +1,61 @@
|
||||
import React, { createContext } from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { useResourceViews, useResourceMeta, useJournals } from 'hooks/query';
|
||||
import { getFieldsFromResourceMeta } from 'utils';
|
||||
|
||||
const ManualJournalsContext = createContext();
|
||||
|
||||
function ManualJournalsListProvider({ query, tableStateChanged, ...props }) {
|
||||
// Fetches accounts resource views and fields.
|
||||
const { data: journalsViews, isLoading: isViewsLoading } =
|
||||
useResourceViews('manual_journals');
|
||||
|
||||
// Fetches the manual journals transactions with pagination meta.
|
||||
const {
|
||||
data: { manualJournals, pagination, filterMeta },
|
||||
isLoading: isManualJournalsLoading,
|
||||
isFetching: isManualJournalsFetching,
|
||||
} = useJournals(query, { keepPreviousData: true });
|
||||
|
||||
// Fetch the accounts resource fields.
|
||||
const {
|
||||
data: resourceMeta,
|
||||
isLoading: isResourceMetaLoading,
|
||||
isFetching: isResourceMetaFetching,
|
||||
} = useResourceMeta('manual_journals');
|
||||
|
||||
// Detarmines the datatable empty status.
|
||||
const isEmptyStatus =
|
||||
isEmpty(manualJournals) && !tableStateChanged && !isManualJournalsLoading;
|
||||
|
||||
// Global state.
|
||||
const state = {
|
||||
manualJournals,
|
||||
pagination,
|
||||
journalsViews,
|
||||
|
||||
resourceMeta,
|
||||
fields: getFieldsFromResourceMeta(resourceMeta.fields),
|
||||
|
||||
isManualJournalsLoading,
|
||||
isManualJournalsFetching,
|
||||
isViewsLoading,
|
||||
|
||||
isEmptyStatus,
|
||||
};
|
||||
|
||||
const isPageLoading =
|
||||
isManualJournalsLoading || isViewsLoading || isResourceMetaLoading;
|
||||
|
||||
return (
|
||||
<DashboardInsider loading={isPageLoading} name={'manual-journals'}>
|
||||
<ManualJournalsContext.Provider value={state} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useManualJournalsContext = () => React.useContext(ManualJournalsContext);
|
||||
|
||||
export { ManualJournalsListProvider, useManualJournalsContext };
|
||||
@@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
|
||||
import { useManualJournalsContext } from './ManualJournalsListProvider';
|
||||
import withManualJournalsActions from './withManualJournalsActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withManualJournals from './withManualJournals';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Manual journal views tabs.
|
||||
*/
|
||||
function ManualJournalsViewTabs({
|
||||
// #withManualJournalsActions
|
||||
setManualJournalsTableState,
|
||||
|
||||
// #withManualJournals
|
||||
journalsTableState
|
||||
}) {
|
||||
// Manual journals context.
|
||||
const { journalsViews } = useManualJournalsContext();
|
||||
|
||||
const tabs = journalsViews.map((view) => ({
|
||||
...pick(view, ['name', 'id']),
|
||||
}));
|
||||
|
||||
const handleClickNewView = () => {};
|
||||
|
||||
// Handles the tab change.
|
||||
const handleTabChange = (viewId) => {
|
||||
setManualJournalsTableState({
|
||||
customViewId: viewId || null,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Navbar className="navbar--dashboard-views">
|
||||
<NavbarGroup align={Alignment.LEFT}>
|
||||
<DashboardViewsTabs
|
||||
resourceName={'manual-journals'}
|
||||
currentViewId={journalsTableState.customViewId}
|
||||
tabs={tabs}
|
||||
onChange={handleTabChange}
|
||||
onNewViewTabClick={handleClickNewView}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withManualJournalsActions,
|
||||
withDashboardActions,
|
||||
withManualJournals(({ manualJournalsTableState }) => ({
|
||||
journalsTableState: manualJournalsTableState,
|
||||
})),
|
||||
)(ManualJournalsViewTabs);
|
||||
174
src/containers/Accounting/JournalsLanding/components.js
Normal file
174
src/containers/Accounting/JournalsLanding/components.js
Normal file
@@ -0,0 +1,174 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Classes,
|
||||
Tooltip,
|
||||
Position,
|
||||
Tag,
|
||||
Button,
|
||||
MenuItem,
|
||||
Menu,
|
||||
MenuDivider,
|
||||
Popover,
|
||||
} from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { FormattedMessage as T, Choose, Money, If, Icon } from 'components';
|
||||
import { safeCallback } from 'utils';
|
||||
|
||||
/**
|
||||
* Amount accessor.
|
||||
*/
|
||||
export const AmountAccessor = (r) => (
|
||||
<Tooltip
|
||||
content={
|
||||
<AmountPopoverContent
|
||||
journalEntries={r.entries}
|
||||
currencyCode={r.currency_code}
|
||||
/>
|
||||
}
|
||||
position={Position.RIGHT_TOP}
|
||||
boundary={'viewport'}
|
||||
>
|
||||
{r.amount_formatted}
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
/**
|
||||
* Amount popover content line.
|
||||
*/
|
||||
export const AmountPopoverContentLine = ({ journalEntry, currencyCode }) => {
|
||||
const isCredit = !!journalEntry.credit;
|
||||
const isDebit = !!journalEntry.debit;
|
||||
const { account } = journalEntry;
|
||||
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={isDebit}>
|
||||
<div>
|
||||
C. <Money amount={journalEntry.debit} currency={currencyCode} /> -{' '}
|
||||
{account.name} <If condition={account.code}>({account.code})</If>
|
||||
</div>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={isCredit}>
|
||||
<div>
|
||||
D. <Money amount={journalEntry.credit} currency={currencyCode} /> -{' '}
|
||||
{account.name} <If condition={account.code}>({account.code})</If>
|
||||
</div>
|
||||
</Choose.When>
|
||||
</Choose>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Amount popover content.
|
||||
*/
|
||||
export function AmountPopoverContent({ journalEntries, currencyCode }) {
|
||||
const journalLinesProps = journalEntries.map((journalEntry) => ({
|
||||
journalEntry,
|
||||
accountId: journalEntry.account_id,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div>
|
||||
{journalLinesProps.map(({ journalEntry, accountId }) => (
|
||||
<AmountPopoverContentLine
|
||||
journalEntry={journalEntry}
|
||||
accountId={accountId}
|
||||
currencyCode={currencyCode}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish column accessor.
|
||||
*/
|
||||
export const StatusAccessor = (row) => {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={!!row.is_published}>
|
||||
<Tag minimal={true}>
|
||||
<T id={'published'} />
|
||||
</Tag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.Otherwise>
|
||||
<Tag minimal={true} intent={Intent.WARNING}>
|
||||
<T id={'draft'} />
|
||||
</Tag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Note column accessor.
|
||||
*/
|
||||
export function NoteAccessor(row) {
|
||||
return (
|
||||
<If condition={row.description}>
|
||||
<Tooltip
|
||||
className={Classes.TOOLTIP_INDICATOR}
|
||||
content={row.description}
|
||||
position={Position.LEFT_TOP}
|
||||
hoverOpenDelay={50}
|
||||
>
|
||||
<Icon icon={'file-alt'} iconSize={16} />
|
||||
</Tooltip>
|
||||
</If>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Table actions cell.
|
||||
*/
|
||||
export const ActionsCell = (props) => {
|
||||
return (
|
||||
<Popover
|
||||
content={<ActionsMenu {...props} />}
|
||||
position={Position.RIGHT_BOTTOM}
|
||||
>
|
||||
<Button icon={<Icon icon="more-h-16" iconSize={16} />} />
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Actions menu of the table.
|
||||
*/
|
||||
export const ActionsMenu = ({
|
||||
payload: { onPublish, onEdit, onDelete, onViewDetails },
|
||||
row: { original },
|
||||
}) => {
|
||||
return (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="reader-18" />}
|
||||
text={intl.get('view_details')}
|
||||
onClick={safeCallback(onViewDetails, original)}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<If condition={!original.is_published}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="arrow-to-top" />}
|
||||
text={intl.get('publish_journal')}
|
||||
onClick={safeCallback(onPublish, original)}
|
||||
/>
|
||||
</If>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('edit_journal')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
<MenuItem
|
||||
text={intl.get('delete_journal')}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
74
src/containers/Accounting/JournalsLanding/utils.js
Normal file
74
src/containers/Accounting/JournalsLanding/utils.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { CLASSES } from '../../../common/classes';
|
||||
import { FormatDateCell } from '../../../components';
|
||||
import { NoteAccessor, StatusAccessor } from './components';
|
||||
|
||||
/**
|
||||
* Retrieve the manual journals columns.
|
||||
*/
|
||||
export const useManualJournalsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'date',
|
||||
Cell: FormatDateCell,
|
||||
width: 115,
|
||||
className: 'date',
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'formatted_amount',
|
||||
width: 115,
|
||||
clickable: true,
|
||||
align: 'right',
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
id: 'journal_number',
|
||||
Header: intl.get('journal_no'),
|
||||
accessor: (row) => `${row.journal_number}`,
|
||||
className: 'journal_number',
|
||||
width: 100,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'journal_type',
|
||||
Header: intl.get('journal_type'),
|
||||
accessor: 'journal_type',
|
||||
width: 110,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
Header: intl.get('publish'),
|
||||
accessor: (row) => StatusAccessor(row),
|
||||
width: 95,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'note',
|
||||
Header: intl.get('note'),
|
||||
accessor: NoteAccessor,
|
||||
disableSortBy: true,
|
||||
width: 85,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'created_at',
|
||||
Header: intl.get('created_at'),
|
||||
accessor: 'created_at',
|
||||
Cell: FormatDateCell,
|
||||
width: 125,
|
||||
clickable: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getManualJournalsTableStateFactory,
|
||||
manualJournalTableStateChangedFactory,
|
||||
} from 'store/manualJournals/manualJournals.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const getJournalsTableQuery = getManualJournalsTableStateFactory();
|
||||
const manualJournalTableStateChanged =
|
||||
manualJournalTableStateChangedFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const mapped = {
|
||||
manualJournalsTableState: getJournalsTableQuery(state, props),
|
||||
manualJournalTableStateChanged: manualJournalTableStateChanged(
|
||||
state,
|
||||
props,
|
||||
),
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
setManualJournalsTableState,
|
||||
} from 'store/manualJournals/manualJournals.actions';
|
||||
|
||||
const mapActionsToProps = (dispatch) => ({
|
||||
setManualJournalsTableState: (queries) =>
|
||||
dispatch(setManualJournalsTableState(queries)),
|
||||
});
|
||||
|
||||
export default connect(null, mapActionsToProps);
|
||||
@@ -0,0 +1,41 @@
|
||||
import * as Yup from 'yup';
|
||||
import intl from 'react-intl-universal';
|
||||
import { DATATYPES_LENGTH } from 'common/dataTypes';
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
journal_number: Yup.string()
|
||||
.required()
|
||||
.min(1)
|
||||
.max(DATATYPES_LENGTH.STRING)
|
||||
.label(intl.get('journal_number_')),
|
||||
journal_type: Yup.string()
|
||||
.required()
|
||||
.min(1)
|
||||
.max(DATATYPES_LENGTH.STRING)
|
||||
.label(intl.get('journal_type')),
|
||||
date: Yup.date()
|
||||
.required()
|
||||
.label(intl.get('date')),
|
||||
currency_code: Yup.string().max(3),
|
||||
publish: Yup.boolean(),
|
||||
reference: Yup.string().nullable().min(1).max(DATATYPES_LENGTH.STRING),
|
||||
description: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(),
|
||||
entries: Yup.array().of(
|
||||
Yup.object().shape({
|
||||
credit: Yup.number().nullable(),
|
||||
debit: Yup.number().nullable(),
|
||||
account_id: Yup.number()
|
||||
.nullable()
|
||||
.when(['credit', 'debit'], {
|
||||
is: (credit, debit) => credit || debit,
|
||||
then: Yup.number().required(),
|
||||
}),
|
||||
contact_id: Yup.number().nullable(),
|
||||
contact_type: Yup.string().nullable(),
|
||||
note: Yup.string().max(DATATYPES_LENGTH.TEXT).nullable(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
export const CreateJournalSchema = Schema;
|
||||
export const EditJournalSchema = Schema;
|
||||
@@ -0,0 +1,42 @@
|
||||
import React from 'react';
|
||||
import { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import MakeJournalEntriesTable from './MakeJournalEntriesTable';
|
||||
import { entriesFieldShouldUpdate, defaultEntry, MIN_LINES_NUMBER } from './utils';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
|
||||
/**
|
||||
* Make journal entries field.
|
||||
*/
|
||||
export default function MakeJournalEntriesField() {
|
||||
const { accounts, contacts } = useMakeJournalFormContext();
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<FastField
|
||||
name={'entries'}
|
||||
contacts={contacts}
|
||||
accounts={accounts}
|
||||
shouldUpdate={entriesFieldShouldUpdate}
|
||||
>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<MakeJournalEntriesTable
|
||||
onChange={(entries) => {
|
||||
setFieldValue('entries', entries);
|
||||
}}
|
||||
entries={value}
|
||||
defaultEntry={defaultEntry}
|
||||
initialLinesNumber={MIN_LINES_NUMBER}
|
||||
error={error}
|
||||
currencyCode={values.currency_code}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
Menu,
|
||||
MenuItem,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import classNames from 'classnames';
|
||||
import { If, Icon } from 'components';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
/**
|
||||
* Make Journal floating actions bar.
|
||||
*/
|
||||
export default function MakeJournalEntriesFooter() {
|
||||
const history = useHistory();
|
||||
|
||||
// Formik context.
|
||||
const { isSubmitting, submitForm } = useFormikContext();
|
||||
|
||||
// Make journal form context.
|
||||
const {
|
||||
manualJournalId,
|
||||
setSubmitPayload,
|
||||
manualJournalPublished = false,
|
||||
} = useMakeJournalFormContext();
|
||||
|
||||
// Handle `submit & publish` button click.
|
||||
const handleSubmitPublishBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: true, publish: true });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handle `submit, publish & new` button click.
|
||||
const handleSubmitPublishAndNewBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, publish: true, resetForm: true });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handle `submit, publish & continue editing` button click.
|
||||
const handleSubmitPublishContinueEditingBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, publish: true });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handle `submit as draft` button click.
|
||||
const handleSubmitDraftBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: true, publish: false });
|
||||
};
|
||||
|
||||
// Handle `submit as draft & new` button click.
|
||||
const handleSubmitDraftAndNewBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, publish: false, resetForm: true });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handles submit as draft & continue editing button click.
|
||||
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
||||
setSubmitPayload({ redirect: false, publish: false });
|
||||
submitForm();
|
||||
};
|
||||
|
||||
// Handle cancel button action click.
|
||||
const handleCancelBtnClick = (event) => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
const handleClearBtnClick = (event) => {};
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||
{/* ----------- Save And Publish ----------- */}
|
||||
<If condition={!manualJournalId || !manualJournalPublished}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitPublishBtnClick}
|
||||
text={<T id={'save_publish'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'publish_and_new'} />}
|
||||
onClick={handleSubmitPublishAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'publish_continue_editing'} />}
|
||||
onClick={handleSubmitPublishContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
{/* ----------- Save As Draft ----------- */}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
className={'ml1'}
|
||||
type="submit"
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
text={<T id={'save_as_draft'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitDraftAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'save_continue_editing'} />}
|
||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
</If>
|
||||
{/* ----------- Save and New ----------- */}
|
||||
<If condition={manualJournalId && manualJournalPublished}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitPublishBtnClick}
|
||||
text={<T id={'save'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitPublishAndNewBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
</If>
|
||||
{/* ----------- Clear & Reset----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
disabled={isSubmitting}
|
||||
onClick={handleClearBtnClick}
|
||||
text={manualJournalId ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
/>
|
||||
{/* ----------- Cancel ----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
onClick={handleCancelBtnClick}
|
||||
text={<T id={'cancel'} />}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
193
src/containers/Accounting/MakeJournal/MakeJournalEntriesForm.js
Normal file
193
src/containers/Accounting/MakeJournal/MakeJournalEntriesForm.js
Normal file
@@ -0,0 +1,193 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import * as R from 'ramda';
|
||||
import { defaultTo, isEmpty, omit } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import {
|
||||
CreateJournalSchema,
|
||||
EditJournalSchema,
|
||||
} from './MakeJournalEntries.schema';
|
||||
import MakeJournalEntriesHeader from './MakeJournalEntriesHeader';
|
||||
import MakeJournalFormFloatingActions from './MakeJournalFormFloatingActions';
|
||||
import MakeJournalEntriesField from './MakeJournalEntriesField';
|
||||
import MakeJournalFormFooter from './MakeJournalFormFooter';
|
||||
import MakeJournalFormDialogs from './MakeJournalFormDialogs';
|
||||
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import withMediaActions from 'containers/Media/withMediaActions';
|
||||
import { compose, orderingLinesIndexes, transactionNumber } from 'utils';
|
||||
import {
|
||||
transformErrors,
|
||||
transformToEditForm,
|
||||
defaultManualJournal,
|
||||
} from './utils';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
|
||||
/**
|
||||
* Journal entries form.
|
||||
*/
|
||||
function MakeJournalEntriesForm({
|
||||
// #withSettings
|
||||
journalNextNumber,
|
||||
journalNumberPrefix,
|
||||
journalAutoIncrement,
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
// Journal form context.
|
||||
const {
|
||||
createJournalMutate,
|
||||
editJournalMutate,
|
||||
isNewMode,
|
||||
manualJournal,
|
||||
submitPayload,
|
||||
} = useMakeJournalFormContext();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
// New journal number.
|
||||
const journalNumber = transactionNumber(
|
||||
journalNumberPrefix,
|
||||
journalNextNumber,
|
||||
);
|
||||
// Form initial values.
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...(!isEmpty(manualJournal)
|
||||
? {
|
||||
...transformToEditForm(manualJournal),
|
||||
}
|
||||
: {
|
||||
...defaultManualJournal,
|
||||
...(journalAutoIncrement && {
|
||||
journal_number: defaultTo(journalNumber, ''),
|
||||
}),
|
||||
currency_code: base_currency,
|
||||
}),
|
||||
}),
|
||||
[manualJournal, base_currency, journalNumber],
|
||||
);
|
||||
|
||||
// Handle the form submiting.
|
||||
const handleSubmit = (values, { setErrors, setSubmitting, resetForm }) => {
|
||||
setSubmitting(true);
|
||||
const entries = values.entries.filter(
|
||||
(entry) => entry.debit || entry.credit,
|
||||
);
|
||||
const getTotal = (type = 'credit') => {
|
||||
return entries.reduce((total, item) => {
|
||||
return item[type] ? item[type] + total : total;
|
||||
}, 0);
|
||||
};
|
||||
const totalCredit = getTotal('credit');
|
||||
const totalDebit = getTotal('debit');
|
||||
|
||||
// Validate the total credit should be eqials total debit.
|
||||
if (totalCredit !== totalDebit) {
|
||||
AppToaster.show({
|
||||
message: intl.get('should_total_of_credit_and_debit_be_equal'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
setSubmitting(false);
|
||||
return;
|
||||
} else if (totalCredit === 0 || totalDebit === 0) {
|
||||
AppToaster.show({
|
||||
message: intl.get('amount_cannot_be_zero_or_empty'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
const form = {
|
||||
...omit(values, ['journal_number', 'journal_number_manually']),
|
||||
...(values.journal_number_manually && {
|
||||
journal_number: values.journal_number,
|
||||
}),
|
||||
entries: R.compose(orderingLinesIndexes)(entries),
|
||||
publish: submitPayload.publish,
|
||||
};
|
||||
|
||||
// Handle the request error.
|
||||
const handleError = ({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
transformErrors(errors, { setErrors });
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
// Handle the request success.
|
||||
const handleSuccess = (errors) => {
|
||||
AppToaster.show({
|
||||
message: intl.get(
|
||||
isNewMode
|
||||
? 'the_journal_has_been_created_successfully'
|
||||
: 'the_journal_has_been_edited_successfully',
|
||||
{ number: values.journal_number },
|
||||
),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
|
||||
if (submitPayload.redirect) {
|
||||
history.push('/manual-journals');
|
||||
}
|
||||
if (submitPayload.resetForm) {
|
||||
resetForm();
|
||||
}
|
||||
};
|
||||
|
||||
if (isNewMode) {
|
||||
createJournalMutate(form).then(handleSuccess).catch(handleError);
|
||||
} else {
|
||||
editJournalMutate([manualJournal.id, form])
|
||||
.then(handleSuccess)
|
||||
.catch(handleError);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
CLASSES.PAGE_FORM,
|
||||
CLASSES.PAGE_FORM_STRIP_STYLE,
|
||||
CLASSES.PAGE_FORM_MAKE_JOURNAL,
|
||||
)}
|
||||
>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={isNewMode ? CreateJournalSchema : EditJournalSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Form>
|
||||
<MakeJournalEntriesHeader />
|
||||
<MakeJournalEntriesField />
|
||||
<MakeJournalFormFooter />
|
||||
<MakeJournalFormFloatingActions />
|
||||
|
||||
{/* --------- Dialogs --------- */}
|
||||
<MakeJournalFormDialogs />
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withMediaActions,
|
||||
withSettings(({ manualJournalsSettings }) => ({
|
||||
journalNextNumber: parseInt(manualJournalsSettings?.nextNumber, 10),
|
||||
journalNumberPrefix: manualJournalsSettings?.numberPrefix,
|
||||
journalAutoIncrement: manualJournalsSettings?.autoIncrement,
|
||||
})),
|
||||
withCurrentOrganization(),
|
||||
)(MakeJournalEntriesForm);
|
||||
@@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import MakeJournalEntriesHeaderFields from './MakeJournalEntriesHeaderFields';
|
||||
import { PageFormBigNumber } from 'components';
|
||||
import { safeSumBy } from 'utils';
|
||||
|
||||
export default function MakeJournalEntriesHeader() {
|
||||
const {
|
||||
values: { entries, currency_code },
|
||||
} = useFormikContext();
|
||||
const totalCredit = safeSumBy(entries, 'credit');
|
||||
const totalDebit = safeSumBy(entries, 'debit');
|
||||
|
||||
const total = Math.max(totalCredit, totalDebit);
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||
<MakeJournalEntriesHeaderFields />
|
||||
|
||||
<PageFormBigNumber
|
||||
label={<T id={'amount'} />}
|
||||
amount={total}
|
||||
currencyCode={currency_code}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
InputGroup,
|
||||
FormGroup,
|
||||
Position,
|
||||
ControlGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import {
|
||||
momentFormatter,
|
||||
compose,
|
||||
inputIntent,
|
||||
handleDateChange,
|
||||
tansformDateValue,
|
||||
} from 'utils';
|
||||
import {
|
||||
Hint,
|
||||
FieldHint,
|
||||
FieldRequiredHint,
|
||||
Icon,
|
||||
InputPrependButton,
|
||||
CurrencySelectList,
|
||||
} from 'components';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import {
|
||||
currenciesFieldShouldUpdate,
|
||||
useObserveJournalNoSettings,
|
||||
} from './utils';
|
||||
/**
|
||||
* Make journal entries header.
|
||||
*/
|
||||
function MakeJournalEntriesHeader({
|
||||
// #ownProps
|
||||
onJournalNumberChanged,
|
||||
|
||||
// #withDialog
|
||||
openDialog,
|
||||
|
||||
// #withSettings
|
||||
journalAutoIncrement,
|
||||
journalNextNumber,
|
||||
journalNumberPrefix,
|
||||
}) {
|
||||
const { currencies } = useMakeJournalFormContext();
|
||||
|
||||
// Handle journal number change.
|
||||
const handleJournalNumberChange = () => {
|
||||
openDialog('journal-number-form', {});
|
||||
};
|
||||
|
||||
// Handle journal number blur.
|
||||
const handleJournalNoBlur = (form, field) => (event) => {
|
||||
const newValue = event.target.value;
|
||||
|
||||
if (field.value !== newValue) {
|
||||
openDialog('journal-number-form', {
|
||||
initialFormValues: {
|
||||
manualTransactionNo: newValue,
|
||||
incrementMode: 'manual-transaction',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useObserveJournalNoSettings(journalNumberPrefix, journalNextNumber);
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/*------------ Posting date -----------*/}
|
||||
<FastField name={'date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'posting_date'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="date" />}
|
||||
minimal={true}
|
||||
inline={true}
|
||||
className={classNames(CLASSES.FILL)}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('date', formattedDate);
|
||||
})}
|
||||
value={tansformDateValue(value)}
|
||||
popoverProps={{
|
||||
position: Position.BOTTOM,
|
||||
minimal: true,
|
||||
}}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Journal number -----------*/}
|
||||
<FastField name={'journal_number'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'journal_no'} />}
|
||||
labelInfo={
|
||||
<>
|
||||
<FieldRequiredHint />
|
||||
<FieldHint />
|
||||
</>
|
||||
}
|
||||
className={'form-group--journal-number'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="journal_number" />}
|
||||
fill={true}
|
||||
inline={true}
|
||||
>
|
||||
<ControlGroup fill={true}>
|
||||
<InputGroup
|
||||
fill={true}
|
||||
value={field.value}
|
||||
asyncControl={true}
|
||||
onBlur={handleJournalNoBlur(form, field)}
|
||||
/>
|
||||
<InputPrependButton
|
||||
buttonProps={{
|
||||
onClick: handleJournalNumberChange,
|
||||
icon: <Icon icon={'settings-18'} />,
|
||||
}}
|
||||
tooltip={true}
|
||||
tooltipProps={{
|
||||
content: (
|
||||
<T id={'setting_your_auto_generated_journal_number'} />
|
||||
),
|
||||
position: Position.BOTTOM_LEFT,
|
||||
}}
|
||||
/>
|
||||
</ControlGroup>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Reference -----------*/}
|
||||
<FastField name={'reference'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference'} />}
|
||||
labelInfo={
|
||||
<Hint
|
||||
content={<T id={'journal_reference_hint'} />}
|
||||
position={Position.RIGHT}
|
||||
/>
|
||||
}
|
||||
className={'form-group--reference'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference" />}
|
||||
fill={true}
|
||||
inline={true}
|
||||
>
|
||||
<InputGroup fill={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Journal type -----------*/}
|
||||
<FastField name={'journal_type'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'journal_type'} />}
|
||||
className={classNames('form-group--account-type', CLASSES.FILL)}
|
||||
inline={true}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
fill={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Currency -----------*/}
|
||||
<FastField
|
||||
name={'currency_code'}
|
||||
currencies={currencies}
|
||||
shouldUpdate={currenciesFieldShouldUpdate}
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'currency'} />}
|
||||
className={classNames('form-group--currency', CLASSES.FILL)}
|
||||
inline={true}
|
||||
>
|
||||
<CurrencySelectList
|
||||
currenciesList={currencies}
|
||||
selectedCurrencyCode={value}
|
||||
onCurrencySelected={(currencyItem) => {
|
||||
form.setFieldValue('currency_code', currencyItem.currency_code);
|
||||
}}
|
||||
defaultSelectText={value}
|
||||
disabled={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withSettings(({ manualJournalsSettings }) => ({
|
||||
journalAutoIncrement: manualJournalsSettings?.autoIncrement,
|
||||
journalNextNumber: manualJournalsSettings?.nextNumber,
|
||||
journalNumberPrefix: manualJournalsSettings?.numberPrefix,
|
||||
})),
|
||||
)(MakeJournalEntriesHeader);
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import 'style/pages/ManualJournal/MakeJournal.scss';
|
||||
|
||||
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
|
||||
import { MakeJournalProvider } from './MakeJournalProvider';
|
||||
|
||||
/**
|
||||
* Make journal entries page.
|
||||
*/
|
||||
export default function MakeJournalEntriesPage() {
|
||||
const { id: journalId } = useParams();
|
||||
|
||||
return (
|
||||
<MakeJournalProvider journalId={journalId}>
|
||||
<MakeJournalEntriesForm />
|
||||
</MakeJournalProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import { DataTableEditable } from 'components';
|
||||
import {
|
||||
compose,
|
||||
saveInvoke,
|
||||
updateMinEntriesLines,
|
||||
updateRemoveLineByIndex,
|
||||
updateAutoAddNewLine,
|
||||
updateTableCell,
|
||||
} from 'utils';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
import { useJournalTableEntriesColumns } from './components';
|
||||
import { updateAdjustEntries } from './utils';
|
||||
|
||||
/**
|
||||
* Make journal entries table component.
|
||||
*/
|
||||
export default function MakeJournalEntriesTable({
|
||||
// #ownPorps
|
||||
onChange,
|
||||
entries,
|
||||
defaultEntry,
|
||||
error,
|
||||
initialLinesNumber = 4,
|
||||
minLinesNumber = 4,
|
||||
currencyCode,
|
||||
}) {
|
||||
const { accounts, contacts } = useMakeJournalFormContext();
|
||||
|
||||
// Memorized data table columns.
|
||||
const columns = useJournalTableEntriesColumns();
|
||||
|
||||
// Handles update datatable data.
|
||||
const handleUpdateData = (rowIndex, columnId, value) => {
|
||||
const newRows = compose(
|
||||
// Auto-adding new lines.
|
||||
updateAutoAddNewLine(defaultEntry, ['account_id', 'credit', 'debit']),
|
||||
// Update items entries total.
|
||||
updateAdjustEntries(rowIndex, columnId, value),
|
||||
// Update entry of the given row index and column id.
|
||||
updateTableCell(rowIndex, columnId, value),
|
||||
)(entries);
|
||||
|
||||
saveInvoke(onChange, newRows);
|
||||
};
|
||||
|
||||
// Handle remove datatable row.
|
||||
const handleRemoveRow = (rowIndex) => {
|
||||
const newRows = compose(
|
||||
// Ensure minimum lines count.
|
||||
updateMinEntriesLines(minLinesNumber, defaultEntry),
|
||||
// Remove the line by the given index.
|
||||
updateRemoveLineByIndex(rowIndex),
|
||||
)(entries);
|
||||
|
||||
saveInvoke(onChange, newRows);
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTableEditable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
sticky={true}
|
||||
totalRow={true}
|
||||
footer={true}
|
||||
payload={{
|
||||
accounts,
|
||||
errors: error,
|
||||
updateData: handleUpdateData,
|
||||
removeRow: handleRemoveRow,
|
||||
contacts,
|
||||
autoFocus: ['account_id', 0],
|
||||
currencyCode,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import JournalNumberDialog from 'containers/Dialogs/JournalNumberDialog';
|
||||
|
||||
/**
|
||||
* Make journal form dialogs.
|
||||
*/
|
||||
export default function MakeJournalFormDialogs() {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
|
||||
// Update the form once the journal number form submit confirm.
|
||||
const handleConfirm = ({ manually, incrementNumber }) => {
|
||||
setFieldValue('journal_number', incrementNumber || '');
|
||||
setFieldValue('journal_number_manually', manually);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<JournalNumberDialog
|
||||
dialogName={'journal-number-form'}
|
||||
onConfirm={handleConfirm}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Intent,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
Menu,
|
||||
MenuItem,
|
||||
} from '@blueprintjs/core';
|
||||
import { useFormikContext } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Icon, If } from 'components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
|
||||
/**
|
||||
* Make Journal floating actions bar.
|
||||
*/
|
||||
export default function MakeJournalFloatingAction() {
|
||||
const history = useHistory();
|
||||
|
||||
// Formik context.
|
||||
const { submitForm, resetForm, isSubmitting } = useFormikContext();
|
||||
|
||||
// Make journal form context.
|
||||
const { setSubmitPayload, manualJournal } = useMakeJournalFormContext();
|
||||
|
||||
// Handle submit & publish button click.
|
||||
const handleSubmitPublishBtnClick = (event) => {
|
||||
submitForm();
|
||||
setSubmitPayload({ redirect: true, publish: true });
|
||||
};
|
||||
|
||||
// Handle submit, publish & new button click.
|
||||
const handleSubmitPublishAndNewBtnClick = (event) => {
|
||||
submitForm();
|
||||
setSubmitPayload({ redirect: false, publish: true, resetForm: true });
|
||||
};
|
||||
|
||||
// Handle submit, publish & edit button click.
|
||||
const handleSubmitPublishContinueEditingBtnClick = (event) => {
|
||||
submitForm();
|
||||
setSubmitPayload({ redirect: false, publish: true });
|
||||
};
|
||||
|
||||
// Handle submit as draft button click.
|
||||
const handleSubmitDraftBtnClick = (event) => {
|
||||
submitForm();
|
||||
setSubmitPayload({ redirect: true, publish: false });
|
||||
};
|
||||
|
||||
// Handle submit as draft & new button click.
|
||||
const handleSubmitDraftAndNewBtnClick = (event) => {
|
||||
submitForm();
|
||||
setSubmitPayload({ redirect: false, publish: false, resetForm: true });
|
||||
};
|
||||
|
||||
// Handle submit as draft & continue editing button click.
|
||||
const handleSubmitDraftContinueEditingBtnClick = (event) => {
|
||||
submitForm();
|
||||
setSubmitPayload({ redirect: false, publish: false });
|
||||
};
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelBtnClick = (event) => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
// Handle clear button click.
|
||||
const handleClearBtnClick = (event) => {
|
||||
resetForm();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||
{/* ----------- Save And Publish ----------- */}
|
||||
<If condition={!manualJournal || !manualJournal?.is_published}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={handleSubmitPublishBtnClick}
|
||||
text={<T id={'save_publish'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'publish_and_new'} />}
|
||||
onClick={handleSubmitPublishAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'publish_continue_editing'} />}
|
||||
onClick={handleSubmitPublishContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
{/* ----------- Save As Draft ----------- */}
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
className={'ml1'}
|
||||
onClick={handleSubmitDraftBtnClick}
|
||||
text={<T id={'save_as_draft'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitDraftAndNewBtnClick}
|
||||
/>
|
||||
<MenuItem
|
||||
text={<T id={'save_continue_editing'} />}
|
||||
onClick={handleSubmitDraftContinueEditingBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
</If>
|
||||
{/* ----------- Save and New ----------- */}
|
||||
<If condition={manualJournal && manualJournal?.is_published}>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting}
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={handleSubmitPublishBtnClick}
|
||||
text={<T id={'save'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
text={<T id={'save_and_new'} />}
|
||||
onClick={handleSubmitPublishAndNewBtnClick}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
</If>
|
||||
{/* ----------- Clear & Reset----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
disabled={isSubmitting}
|
||||
onClick={handleClearBtnClick}
|
||||
text={manualJournal ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
/>
|
||||
{/* ----------- Cancel ----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
onClick={handleCancelBtnClick}
|
||||
text={<T id={'cancel'} />}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { Postbox, ErrorMessage, Row, Col } from 'components';
|
||||
import Dragzone from 'components/Dragzone';
|
||||
import { inputIntent } from 'utils';
|
||||
|
||||
export default function MakeJournalFormFooter() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Postbox title={<T id={'journal_details'} />} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="description" />}
|
||||
fill={true}
|
||||
>
|
||||
<TextArea fill={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={<T id={'attachments_maximum'} />}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
88
src/containers/Accounting/MakeJournal/MakeJournalProvider.js
Normal file
88
src/containers/Accounting/MakeJournal/MakeJournalProvider.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import React, { createContext, useState } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import {
|
||||
useAccounts,
|
||||
useAutoCompleteContacts,
|
||||
useCurrencies,
|
||||
useJournal,
|
||||
useCreateJournal,
|
||||
useEditJournal,
|
||||
useSettings
|
||||
} from 'hooks/query';
|
||||
|
||||
const MakeJournalFormContext = createContext();
|
||||
|
||||
/**
|
||||
* Make journal form provider.
|
||||
*/
|
||||
function MakeJournalProvider({ journalId, ...props }) {
|
||||
// Load the accounts list.
|
||||
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
|
||||
|
||||
// Load the customers list.
|
||||
const {
|
||||
data: contacts,
|
||||
isLoading: isContactsLoading,
|
||||
} = useAutoCompleteContacts();
|
||||
|
||||
// Load the currencies list.
|
||||
const { data: currencies, isLoading: isCurrenciesLoading } = useCurrencies();
|
||||
|
||||
// Load the details of the given manual journal.
|
||||
const { data: manualJournal, isLoading: isJournalLoading } = useJournal(
|
||||
journalId,
|
||||
{
|
||||
enabled: !!journalId,
|
||||
},
|
||||
);
|
||||
// Create and edit journal mutations.
|
||||
const { mutateAsync: createJournalMutate } = useCreateJournal();
|
||||
const { mutateAsync: editJournalMutate } = useEditJournal();
|
||||
|
||||
// Loading the journal settings.
|
||||
const { isLoading: isSettingsLoading } = useSettings();
|
||||
|
||||
// Submit form payload.
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
const provider = {
|
||||
accounts,
|
||||
contacts,
|
||||
currencies,
|
||||
manualJournal,
|
||||
|
||||
createJournalMutate,
|
||||
editJournalMutate,
|
||||
|
||||
isAccountsLoading,
|
||||
isContactsLoading,
|
||||
isCurrenciesLoading,
|
||||
isJournalLoading,
|
||||
isSettingsLoading,
|
||||
|
||||
isNewMode: !journalId,
|
||||
|
||||
submitPayload,
|
||||
setSubmitPayload
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
isJournalLoading ||
|
||||
isAccountsLoading ||
|
||||
isCurrenciesLoading ||
|
||||
isContactsLoading ||
|
||||
isSettingsLoading
|
||||
}
|
||||
name={'make-journal-page'}
|
||||
>
|
||||
<MakeJournalFormContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useMakeJournalFormContext = () =>
|
||||
React.useContext(MakeJournalFormContext);
|
||||
|
||||
export { MakeJournalProvider, useMakeJournalFormContext };
|
||||
179
src/containers/Accounting/MakeJournal/components.js
Normal file
179
src/containers/Accounting/MakeJournal/components.js
Normal file
@@ -0,0 +1,179 @@
|
||||
import React from 'react';
|
||||
import { Intent, Position, Button, Tooltip } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { Icon, Money, Hint } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import {
|
||||
AccountsListFieldCell,
|
||||
MoneyFieldCell,
|
||||
InputGroupCell,
|
||||
ContactsListFieldCell,
|
||||
} from 'components/DataTableCells';
|
||||
import { safeSumBy } from 'utils';
|
||||
|
||||
/**
|
||||
* Contact header cell.
|
||||
*/
|
||||
export function ContactHeaderCell() {
|
||||
return (
|
||||
<>
|
||||
<T id={'contact'} />
|
||||
<Hint
|
||||
content={<T id={'contact_column_hint'} />}
|
||||
position={Position.LEFT_BOTTOM}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Credit header cell.
|
||||
*/
|
||||
export function CreditHeaderCell({ payload: { currencyCode } }) {
|
||||
return intl.get('credit_currency', { currency: currencyCode });
|
||||
}
|
||||
|
||||
/**
|
||||
* debit header cell.
|
||||
*/
|
||||
export function DebitHeaderCell({ payload: { currencyCode } }) {
|
||||
return intl.get('debit_currency', { currency: currencyCode });
|
||||
}
|
||||
|
||||
/**
|
||||
* Account footer cell.
|
||||
*/
|
||||
function AccountFooterCell({ payload: { currencyCode } }) {
|
||||
return (
|
||||
<span>
|
||||
{intl.get('total_currency', { currency: currencyCode })}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Total credit table footer cell.
|
||||
*/
|
||||
function TotalCreditFooterCell({ payload: { currencyCode }, rows }) {
|
||||
const credit = safeSumBy(rows, 'original.credit');
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Money amount={credit} currency={currencyCode} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Total debit table footer cell.
|
||||
*/
|
||||
function TotalDebitFooterCell({ payload: { currencyCode }, rows }) {
|
||||
const debit = safeSumBy(rows, 'original.debit');
|
||||
|
||||
return (
|
||||
<span>
|
||||
<Money amount={debit} currency={currencyCode} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Actions cell renderer.
|
||||
*/
|
||||
export const ActionsCellRenderer = ({
|
||||
row: { index },
|
||||
column: { id },
|
||||
cell: { value: initialValue },
|
||||
data,
|
||||
payload,
|
||||
}) => {
|
||||
const onClickRemoveRole = () => {
|
||||
payload.removeRow(index);
|
||||
};
|
||||
return (
|
||||
<Tooltip content={<T id={'remove_the_line'} />} position={Position.LEFT}>
|
||||
<Button
|
||||
icon={<Icon icon="times-circle" iconSize={14} />}
|
||||
iconSize={14}
|
||||
className="ml2"
|
||||
minimal={true}
|
||||
intent={Intent.DANGER}
|
||||
onClick={onClickRemoveRole}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve columns of make journal entries table.
|
||||
*/
|
||||
export const useJournalTableEntriesColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: '#',
|
||||
accessor: 'index',
|
||||
Cell: ({ row: { index } }) => <span>{index + 1}</span>,
|
||||
className: 'index',
|
||||
width: 40,
|
||||
disableResizing: true,
|
||||
disableSortBy: true,
|
||||
sticky: 'left',
|
||||
},
|
||||
{
|
||||
Header: intl.get('account'),
|
||||
id: 'account_id',
|
||||
accessor: 'account_id',
|
||||
Cell: AccountsListFieldCell,
|
||||
Footer: AccountFooterCell,
|
||||
className: 'account',
|
||||
disableSortBy: true,
|
||||
width: 160,
|
||||
},
|
||||
{
|
||||
Header: CreditHeaderCell,
|
||||
accessor: 'credit',
|
||||
Cell: MoneyFieldCell,
|
||||
Footer: TotalCreditFooterCell,
|
||||
className: 'credit',
|
||||
disableSortBy: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
Header: DebitHeaderCell,
|
||||
accessor: 'debit',
|
||||
Cell: MoneyFieldCell,
|
||||
Footer: TotalDebitFooterCell,
|
||||
className: 'debit',
|
||||
disableSortBy: true,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
Header: ContactHeaderCell,
|
||||
id: 'contact_id',
|
||||
accessor: 'contact_id',
|
||||
Cell: ContactsListFieldCell,
|
||||
className: 'contact',
|
||||
disableSortBy: true,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
Header: intl.get('note'),
|
||||
accessor: 'note',
|
||||
Cell: InputGroupCell,
|
||||
disableSortBy: true,
|
||||
className: 'note',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
Header: '',
|
||||
accessor: 'action',
|
||||
Cell: ActionsCellRenderer,
|
||||
className: 'actions',
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
width: 45,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
194
src/containers/Accounting/MakeJournal/utils.js
Normal file
194
src/containers/Accounting/MakeJournal/utils.js
Normal file
@@ -0,0 +1,194 @@
|
||||
import React from 'react';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { sumBy, setWith, toSafeInteger, get } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import {
|
||||
transactionNumber,
|
||||
updateTableCell,
|
||||
repeatValue,
|
||||
transformToForm,
|
||||
defaultFastFieldShouldUpdate,
|
||||
ensureEntriesHasEmptyLine
|
||||
} from 'utils';
|
||||
import { AppToaster } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { useFormikContext } from 'formik';
|
||||
|
||||
const ERROR = {
|
||||
JOURNAL_NUMBER_ALREADY_EXISTS: 'JOURNAL.NUMBER.ALREADY.EXISTS',
|
||||
CUSTOMERS_NOT_WITH_RECEVIABLE_ACC: 'CUSTOMERS.NOT.WITH.RECEIVABLE.ACCOUNT',
|
||||
VENDORS_NOT_WITH_PAYABLE_ACCOUNT: 'VENDORS.NOT.WITH.PAYABLE.ACCOUNT',
|
||||
PAYABLE_ENTRIES_HAS_NO_VENDORS: 'PAYABLE.ENTRIES.HAS.NO.VENDORS',
|
||||
RECEIVABLE_ENTRIES_HAS_NO_CUSTOMERS: 'RECEIVABLE.ENTRIES.HAS.NO.CUSTOMERS',
|
||||
CREDIT_DEBIT_SUMATION_SHOULD_NOT_EQUAL_ZERO:
|
||||
'CREDIT.DEBIT.SUMATION.SHOULD.NOT.EQUAL.ZERO',
|
||||
ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
|
||||
};
|
||||
|
||||
export const MIN_LINES_NUMBER = 4;
|
||||
|
||||
export const defaultEntry = {
|
||||
account_id: '',
|
||||
credit: '',
|
||||
debit: '',
|
||||
contact_id: '',
|
||||
note: '',
|
||||
};
|
||||
|
||||
export const defaultManualJournal = {
|
||||
journal_number: '',
|
||||
journal_type: 'Journal',
|
||||
date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
description: '',
|
||||
reference: '',
|
||||
currency_code: '',
|
||||
publish: '',
|
||||
entries: [...repeatValue(defaultEntry, 4)],
|
||||
};
|
||||
|
||||
// Transform to edit form.
|
||||
export function transformToEditForm(manualJournal) {
|
||||
const defaultEntry = defaultManualJournal.entries[0];
|
||||
const initialEntries = [
|
||||
...manualJournal.entries.map((entry) => ({
|
||||
...transformToForm(entry, defaultEntry),
|
||||
})),
|
||||
...repeatValue(
|
||||
defaultEntry,
|
||||
Math.max(MIN_LINES_NUMBER - manualJournal.entries.length, 0),
|
||||
),
|
||||
];
|
||||
|
||||
const entries = R.compose(
|
||||
ensureEntriesHasEmptyLine(MIN_LINES_NUMBER, defaultEntry),
|
||||
)(initialEntries);
|
||||
|
||||
return {
|
||||
...transformToForm(manualJournal, defaultManualJournal),
|
||||
entries,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Entries adjustment.
|
||||
*/
|
||||
function adjustmentEntries(entries) {
|
||||
const credit = sumBy(entries, (e) => toSafeInteger(e.credit));
|
||||
const debit = sumBy(entries, (e) => toSafeInteger(e.debit));
|
||||
|
||||
return {
|
||||
debit: Math.max(credit - debit, 0),
|
||||
credit: Math.max(debit - credit, 0),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjustment credit/debit entries.
|
||||
* @param {number} rowIndex
|
||||
* @param {number} columnId
|
||||
* @param {string} value
|
||||
* @return {array}
|
||||
*/
|
||||
export const updateAdjustEntries = (rowIndex, columnId, value) => (rows) => {
|
||||
let newRows = [...rows];
|
||||
|
||||
const oldCredit = get(rows, `[${rowIndex}].credit`);
|
||||
const oldDebit = get(rows, `[${rowIndex}].debit`);
|
||||
|
||||
if (columnId === 'account_id' && !oldCredit && !oldDebit) {
|
||||
const adjustment = adjustmentEntries(rows);
|
||||
|
||||
if (adjustment.credit) {
|
||||
newRows = updateTableCell(rowIndex, 'credit', adjustment.credit)(newRows);
|
||||
}
|
||||
if (adjustment.debit) {
|
||||
newRows = updateTableCell(rowIndex, 'debit', adjustment.debit)(newRows);
|
||||
}
|
||||
}
|
||||
return newRows;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform API errors in toasts messages.
|
||||
*/
|
||||
export const transformErrors = (resErrors, { setErrors, errors }) => {
|
||||
const getError = (errorType) => resErrors.find((e) => e.type === errorType);
|
||||
const toastMessages = [];
|
||||
let error;
|
||||
let newErrors = { ...errors, entries: [] };
|
||||
|
||||
const setEntriesErrors = (indexes, prop, message) =>
|
||||
indexes.forEach((i) => {
|
||||
const index = Math.max(i - 1, 0);
|
||||
newErrors = setWith(newErrors, `entries.[${index}].${prop}`, message);
|
||||
});
|
||||
|
||||
if ((error = getError(ERROR.RECEIVABLE_ENTRIES_HAS_NO_CUSTOMERS))) {
|
||||
toastMessages.push(
|
||||
intl.get('should_select_customers_with_entries_have_receivable_account'),
|
||||
);
|
||||
setEntriesErrors(error.indexes, 'contact_id', 'error');
|
||||
}
|
||||
if ((error = getError(ERROR.ENTRIES_SHOULD_ASSIGN_WITH_CONTACT))) {
|
||||
if (error.meta.find((meta) => meta.contact_type === 'customer')) {
|
||||
toastMessages.push(
|
||||
intl.get('receivable_accounts_should_assign_with_customers'),
|
||||
);
|
||||
}
|
||||
if (error.meta.find((meta) => meta.contact_type === 'vendor')) {
|
||||
toastMessages.push(
|
||||
intl.get('payable_accounts_should_assign_with_vendors'),
|
||||
);
|
||||
}
|
||||
const indexes = error.meta.map((meta) => meta.indexes).flat();
|
||||
setEntriesErrors(indexes, 'contact_id', 'error');
|
||||
}
|
||||
if ((error = getError(ERROR.JOURNAL_NUMBER_ALREADY_EXISTS))) {
|
||||
newErrors = setWith(
|
||||
newErrors,
|
||||
'journal_number',
|
||||
intl.get('journal_number_is_already_used'),
|
||||
);
|
||||
}
|
||||
setErrors({ ...newErrors });
|
||||
|
||||
if (toastMessages.length > 0) {
|
||||
AppToaster.show({
|
||||
message: toastMessages.map((message) => {
|
||||
return <div>{message}</div>;
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const useObserveJournalNoSettings = (prefix, nextNumber) => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
|
||||
React.useEffect(() => {
|
||||
const journalNo = transactionNumber(prefix, nextNumber);
|
||||
setFieldValue('journal_number', journalNo);
|
||||
}, [setFieldValue, prefix, nextNumber]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines entries fast field should update.
|
||||
*/
|
||||
export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.accounts !== oldProps.accounts ||
|
||||
newProps.contacts !== oldProps.contacts ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Detarmines currencies fast field should update.
|
||||
*/
|
||||
export const currenciesFieldShouldUpdate = (newProps, oldProps) => {
|
||||
return (
|
||||
newProps.currencies !== oldProps.currencies ||
|
||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||
);
|
||||
};
|
||||
47
src/containers/Accounting/ManualJournalUniversalSearch.js
Normal file
47
src/containers/Accounting/ManualJournalUniversalSearch.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import intl from 'react-intl-universal';
|
||||
import { RESOURCES_TYPES } from 'common/resourcesTypes';
|
||||
import withDrawerActions from '../Drawer/withDrawerActions';
|
||||
|
||||
/**
|
||||
* Universal search manual journal item select action.
|
||||
*/
|
||||
function JournalUniversalSearchSelectComponent({
|
||||
// #ownProps
|
||||
resourceType,
|
||||
resourceId,
|
||||
onAction,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
if (resourceType === RESOURCES_TYPES.MANUAL_JOURNAL) {
|
||||
openDrawer('journal-drawer', { manualJournalId: resourceId });
|
||||
onAction && onAction();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const JournalUniversalSearchSelectAction = withDrawerActions(
|
||||
JournalUniversalSearchSelectComponent,
|
||||
);
|
||||
|
||||
/**
|
||||
* Mappes the manual journal item to search item.
|
||||
*/
|
||||
const manualJournalsToSearch = (manualJournal) => ({
|
||||
id: manualJournal.id,
|
||||
text: manualJournal.journal_number,
|
||||
subText: manualJournal.formatted_date,
|
||||
label: manualJournal.formatted_amount,
|
||||
reference: manualJournal,
|
||||
});
|
||||
|
||||
/**
|
||||
* Binds universal search invoice configure.
|
||||
*/
|
||||
export const universalSearchJournalBind = () => ({
|
||||
resourceType: RESOURCES_TYPES.MANUAL_JOURNAL,
|
||||
optionItemLabel: intl.get('manual_journals'),
|
||||
selectItemAction: JournalUniversalSearchSelectAction,
|
||||
itemSelect: manualJournalsToSearch,
|
||||
});
|
||||
45
src/containers/Accounts/AccountUniversalSearch.js
Normal file
45
src/containers/Accounts/AccountUniversalSearch.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import intl from 'react-intl-universal';
|
||||
import { RESOURCES_TYPES } from '../../common/resourcesTypes';
|
||||
import withDrawerActions from '../Drawer/withDrawerActions';
|
||||
|
||||
function AccountUniversalSearchItemSelectComponent({
|
||||
// #ownProps
|
||||
resourceType,
|
||||
resourceId,
|
||||
onAction,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
if (resourceType === RESOURCES_TYPES.ACCOUNT) {
|
||||
openDrawer('account-drawer', { accountId: resourceId });
|
||||
onAction && onAction();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const AccountUniversalSearchItemSelect = withDrawerActions(
|
||||
AccountUniversalSearchItemSelectComponent,
|
||||
);
|
||||
|
||||
/**
|
||||
* Transformes account item to search item.
|
||||
* @param {*} account
|
||||
* @returns
|
||||
*/
|
||||
const accountToSearch = (account) => ({
|
||||
id: account.id,
|
||||
text: `${account.name} - ${account.code}`,
|
||||
label: account.formatted_amount,
|
||||
reference: account,
|
||||
});
|
||||
|
||||
/**
|
||||
* Binds universal search account configure.
|
||||
*/
|
||||
export const universalSearchAccountBind = () => ({
|
||||
resourceType: RESOURCES_TYPES.ACCOUNT,
|
||||
optionItemLabel: intl.get('accounts'),
|
||||
selectItemAction: AccountUniversalSearchItemSelect,
|
||||
itemSelect: accountToSearch,
|
||||
});
|
||||
194
src/containers/Accounts/AccountsActionsBar.js
Normal file
194
src/containers/Accounts/AccountsActionsBar.js
Normal file
@@ -0,0 +1,194 @@
|
||||
import React from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
Button,
|
||||
NavbarGroup,
|
||||
Classes,
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
Switch,
|
||||
Alignment,
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import {
|
||||
AdvancedFilterPopover,
|
||||
If,
|
||||
DashboardActionViewsList,
|
||||
DashboardFilterButton,
|
||||
} from 'components';
|
||||
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
|
||||
import { useRefreshAccounts } from 'hooks/query/accounts';
|
||||
import { useAccountsChartContext } from 'containers/Accounts/AccountsChartProvider';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAccountsTableActions from './withAccountsTableActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Accounts actions bar.
|
||||
*/
|
||||
function AccountsActionsBar({
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #withAccounts
|
||||
accountsSelectedRows,
|
||||
accountsInactiveMode,
|
||||
accountsFilterConditions,
|
||||
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
// #withAccountsTableActions
|
||||
setAccountsTableState,
|
||||
|
||||
// #ownProps
|
||||
onFilterChanged,
|
||||
}) {
|
||||
const { resourceViews, fields } = useAccountsChartContext();
|
||||
|
||||
const onClickNewAccount = () => {
|
||||
openDialog('account-form', {});
|
||||
};
|
||||
|
||||
// Accounts refresh action.
|
||||
const { refresh } = useRefreshAccounts();
|
||||
|
||||
// Handle bulk accounts delete.
|
||||
const handleBulkDelete = () => {
|
||||
openAlert('accounts-bulk-delete', { accountsIds: accountsSelectedRows });
|
||||
};
|
||||
|
||||
// Handle bulk accounts activate.
|
||||
const handelBulkActivate = () => {
|
||||
openAlert('accounts-bulk-activate', { accountsIds: accountsSelectedRows });
|
||||
};
|
||||
|
||||
// Handle bulk accounts inactivate.
|
||||
const handelBulkInactive = () => {
|
||||
openAlert('accounts-bulk-inactivate', {
|
||||
accountsIds: accountsSelectedRows,
|
||||
});
|
||||
};
|
||||
|
||||
// Handle tab changing.
|
||||
const handleTabChange = (view) => {
|
||||
setAccountsTableState({ viewSlug: view ? view.slug : null });
|
||||
};
|
||||
|
||||
// Handle inactive switch changing.
|
||||
const handleInactiveSwitchChange = (event) => {
|
||||
const checked = event.target.checked;
|
||||
setAccountsTableState({ inactiveMode: checked });
|
||||
};
|
||||
|
||||
// Handle click a refresh accounts
|
||||
const handleRefreshBtnClick = () => {
|
||||
refresh();
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<DashboardActionViewsList
|
||||
resourceName={'accounts'}
|
||||
allMenuItem={true}
|
||||
allMenuItemText={<T id={'all_accounts'} />}
|
||||
views={resourceViews}
|
||||
onChange={handleTabChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="plus" />}
|
||||
text={<T id={'new_account'} />}
|
||||
onClick={onClickNewAccount}
|
||||
/>
|
||||
<AdvancedFilterPopover
|
||||
advancedFilterProps={{
|
||||
conditions: accountsFilterConditions,
|
||||
defaultFieldKey: 'name',
|
||||
fields: fields,
|
||||
onFilterChange: (filterConditions) => {
|
||||
setAccountsTableState({ filterRoles: filterConditions });
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DashboardFilterButton
|
||||
conditionsCount={accountsFilterConditions.length}
|
||||
/>
|
||||
</AdvancedFilterPopover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<If condition={!isEmpty(accountsSelectedRows)}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="play-16" iconSize={16} />}
|
||||
text={<T id={'activate'} />}
|
||||
onClick={handelBulkActivate}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="pause-16" iconSize={16} />}
|
||||
text={<T id={'inactivate'} />}
|
||||
onClick={handelBulkInactive}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleBulkDelete}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
text={<T id={'print'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||
text={<T id={'import'} />}
|
||||
/>
|
||||
<Switch
|
||||
labelElement={<T id={'inactive'} />}
|
||||
defaultChecked={accountsInactiveMode}
|
||||
onChange={handleInactiveSwitchChange}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align={Alignment.RIGHT}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="refresh-16" iconSize={14} />}
|
||||
onClick={handleRefreshBtnClick}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withAlertActions,
|
||||
withAccounts(({ accountsSelectedRows, accountsTableState }) => ({
|
||||
accountsSelectedRows,
|
||||
accountsInactiveMode: accountsTableState.inactiveMode,
|
||||
accountsFilterConditions: accountsTableState.filterRoles,
|
||||
})),
|
||||
withAccountsTableActions,
|
||||
)(AccountsActionsBar);
|
||||
26
src/containers/Accounts/AccountsAlerts.js
Normal file
26
src/containers/Accounts/AccountsAlerts.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import AccountDeleteAlert from 'containers/Alerts/AccountDeleteAlert';
|
||||
import AccountInactivateAlert from 'containers/Alerts/AccountInactivateAlert';
|
||||
import AccountActivateAlert from 'containers/Alerts/AccountActivateAlert';
|
||||
// import AccountBulkDeleteAlert from 'containers/Alerts/AccountBulkDeleteAlert';
|
||||
// import AccountBulkInactivateAlert from 'containers/Alerts/AccountBulkInactivateAlert';
|
||||
// import AccountBulkActivateAlert from 'containers/Alerts/AccountBulkActivateAlert';
|
||||
|
||||
/**
|
||||
* Accounts alert.
|
||||
*/
|
||||
export default function AccountsAlerts({
|
||||
|
||||
}) {
|
||||
return (
|
||||
<div class="accounts-alerts">
|
||||
<AccountDeleteAlert name={'account-delete'} />
|
||||
<AccountInactivateAlert name={'account-inactivate'} />
|
||||
<AccountActivateAlert name={'account-activate'} />
|
||||
|
||||
{/* <AccountBulkDeleteAlert name={'accounts-bulk-delete'} />
|
||||
<AccountBulkInactivateAlert name={'accounts-bulk-inactivate'} />
|
||||
<AccountBulkActivateAlert name={'accounts-bulk-activate'} /> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
64
src/containers/Accounts/AccountsChart.js
Normal file
64
src/containers/Accounts/AccountsChart.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import 'style/pages/Accounts/List.scss';
|
||||
import { DashboardPageContent, DashboardContentTable } from 'components';
|
||||
|
||||
import { AccountsChartProvider } from './AccountsChartProvider';
|
||||
|
||||
import AccountsViewsTabs from 'containers/Accounts/AccountsViewsTabs';
|
||||
import AccountsActionsBar from 'containers/Accounts/AccountsActionsBar';
|
||||
import AccountsAlerts from './AccountsAlerts';
|
||||
import AccountsDataTable from './AccountsDataTable';
|
||||
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { transformAccountsStateToQuery } from './utils';
|
||||
import withAccountsTableActions from './withAccountsTableActions';
|
||||
|
||||
/**
|
||||
* Accounts chart list.
|
||||
*/
|
||||
function AccountsChart({
|
||||
// #withAccounts
|
||||
accountsTableState,
|
||||
accountsTableStateChanged,
|
||||
|
||||
// #withAccountsActions
|
||||
resetAccountsTableState,
|
||||
}) {
|
||||
// Resets the accounts table state once the page unmount.
|
||||
useEffect(
|
||||
() => () => {
|
||||
resetAccountsTableState();
|
||||
},
|
||||
[resetAccountsTableState],
|
||||
);
|
||||
|
||||
return (
|
||||
<AccountsChartProvider
|
||||
query={transformAccountsStateToQuery(accountsTableState)}
|
||||
tableStateChanged={accountsTableStateChanged}
|
||||
>
|
||||
<AccountsActionsBar />
|
||||
|
||||
<DashboardPageContent>
|
||||
<AccountsViewsTabs />
|
||||
|
||||
<DashboardContentTable>
|
||||
<AccountsDataTable />
|
||||
</DashboardContentTable>
|
||||
</DashboardPageContent>
|
||||
|
||||
<AccountsAlerts />
|
||||
</AccountsChartProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAccounts(({ accountsTableState, accountsTableStateChanged }) => ({
|
||||
accountsTableState,
|
||||
accountsTableStateChanged,
|
||||
})),
|
||||
withAccountsTableActions,
|
||||
)(AccountsChart);
|
||||
58
src/containers/Accounts/AccountsChartProvider.js
Normal file
58
src/containers/Accounts/AccountsChartProvider.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import React, { createContext } from 'react';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import { useResourceViews, useResourceMeta, useAccounts } from 'hooks/query';
|
||||
import { getFieldsFromResourceMeta } from 'utils';
|
||||
|
||||
const AccountsChartContext = createContext();
|
||||
|
||||
/**
|
||||
* Accounts chart data provider.
|
||||
*/
|
||||
function AccountsChartProvider({ query, tableStateChanged,...props }) {
|
||||
// Fetch accounts resource views and fields.
|
||||
const { data: resourceViews, isLoading: isViewsLoading } =
|
||||
useResourceViews('accounts');
|
||||
|
||||
// Fetch the accounts resource fields.
|
||||
const {
|
||||
data: resourceMeta,
|
||||
isLoading: isResourceMetaLoading,
|
||||
isFetching: isResourceMetaFetching,
|
||||
} = useResourceMeta('accounts');
|
||||
|
||||
// Fetch accounts list according to the given custom view id.
|
||||
const {
|
||||
data: accounts,
|
||||
isFetching: isAccountsFetching,
|
||||
isLoading: isAccountsLoading,
|
||||
} = useAccounts(query, { keepPreviousData: true });
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
accounts,
|
||||
|
||||
resourceMeta,
|
||||
resourceViews,
|
||||
|
||||
fields: getFieldsFromResourceMeta(resourceMeta.fields),
|
||||
|
||||
isAccountsLoading,
|
||||
isAccountsFetching,
|
||||
isResourceMetaFetching,
|
||||
isResourceMetaLoading,
|
||||
isViewsLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={isViewsLoading || isResourceMetaLoading}
|
||||
name={'accounts-chart'}
|
||||
>
|
||||
<AccountsChartContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const useAccountsChartContext = () => React.useContext(AccountsChartContext);
|
||||
|
||||
export { AccountsChartProvider, useAccountsChartContext };
|
||||
126
src/containers/Accounts/AccountsDataTable.js
Normal file
126
src/containers/Accounts/AccountsDataTable.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import React from 'react';
|
||||
|
||||
import { TableFastCell, DataTable } from 'components';
|
||||
import { compose } from 'utils';
|
||||
|
||||
import { useAccountsTableColumns, rowClassNames } from './utils';
|
||||
import { ActionsMenu } from './components';
|
||||
import { TABLES } from 'common/tables';
|
||||
|
||||
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
||||
|
||||
import { useAccountsChartContext } from './AccountsChartProvider';
|
||||
import { useMemorizedColumnsWidths } from '../../hooks';
|
||||
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
/**
|
||||
* Accounts data-table.
|
||||
*/
|
||||
function AccountsDataTable({
|
||||
// #withAlertsDialog
|
||||
openAlert,
|
||||
|
||||
// #withDial
|
||||
openDialog,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
const { isAccountsLoading, isAccountsFetching, accounts } =
|
||||
useAccountsChartContext();
|
||||
|
||||
// Retrieve accounts table columns.
|
||||
const columns = useAccountsTableColumns();
|
||||
|
||||
// Handle delete action account.
|
||||
const handleDeleteAccount = (account) => {
|
||||
openAlert('account-delete', { accountId: account.id });
|
||||
};
|
||||
|
||||
// Handle activate action account.
|
||||
const handleActivateAccount = (account) => {
|
||||
openAlert('account-activate', { accountId: account.id });
|
||||
};
|
||||
|
||||
// Handle inactivate action account.
|
||||
const handleInactivateAccount = (account) => {
|
||||
openAlert('account-inactivate', { accountId: account.id });
|
||||
};
|
||||
|
||||
// Handle edit account action.
|
||||
const handleEditAccount = (account) => {
|
||||
openDialog('account-form', { action: 'edit', id: account.id });
|
||||
};
|
||||
|
||||
// Handle view detail account.
|
||||
const handleViewDetailAccount = ({ id }) => {
|
||||
openDrawer('account-drawer', { accountId: id });
|
||||
};
|
||||
|
||||
// Handle new child button click.
|
||||
const handleNewChildAccount = (account) => {
|
||||
openDialog('account-form', {
|
||||
action: 'new_child',
|
||||
parentAccountId: account.id,
|
||||
accountType: account.account_type,
|
||||
});
|
||||
};
|
||||
// Handle cell click.
|
||||
const handleCellClick = (cell, event) => {
|
||||
openDrawer('account-drawer', { accountId: cell.row.original.id });
|
||||
};
|
||||
// Local storage memorizing columns widths.
|
||||
const [initialColumnsWidths, , handleColumnResizing] =
|
||||
useMemorizedColumnsWidths(TABLES.ACCOUNTS);
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
noInitialFetch={true}
|
||||
columns={columns}
|
||||
data={accounts}
|
||||
selectionColumn={true}
|
||||
expandable={true}
|
||||
sticky={true}
|
||||
loading={isAccountsLoading}
|
||||
headerLoading={isAccountsLoading}
|
||||
progressBarLoading={isAccountsFetching}
|
||||
rowClassNames={rowClassNames}
|
||||
autoResetExpanded={false}
|
||||
autoResetSortBy={false}
|
||||
autoResetSelectedRows={false}
|
||||
expandColumnSpace={1}
|
||||
expandToggleColumn={2}
|
||||
selectionColumnWidth={45}
|
||||
TableCellRenderer={TableFastCell}
|
||||
TableRowsRenderer={TableVirtualizedListRows}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||
ContextMenu={ActionsMenu}
|
||||
// #TableVirtualizedListRows props.
|
||||
vListrowHeight={42}
|
||||
vListOverscanRowCount={0}
|
||||
onCellClick={handleCellClick}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
payload={{
|
||||
onEdit: handleEditAccount,
|
||||
onDelete: handleDeleteAccount,
|
||||
onActivate: handleActivateAccount,
|
||||
onInactivate: handleInactivateAccount,
|
||||
onNewChild: handleNewChildAccount,
|
||||
onViewDetails: handleViewDetailAccount,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
withDialogActions,
|
||||
)(AccountsDataTable);
|
||||
59
src/containers/Accounts/AccountsViewsTabs.js
Normal file
59
src/containers/Accounts/AccountsViewsTabs.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Alignment, Navbar, NavbarGroup } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { DashboardViewsTabs } from 'components';
|
||||
import { useAccountsChartContext } from 'containers/Accounts/AccountsChartProvider';
|
||||
|
||||
import withAccountsTableActions from './withAccountsTableActions';
|
||||
import withAccounts from './withAccounts';
|
||||
|
||||
import { compose, transfromViewsToTabs } from 'utils';
|
||||
|
||||
/**
|
||||
* Accounts views tabs.
|
||||
*/
|
||||
function AccountsViewsTabs({
|
||||
// #withAccountsTableActions
|
||||
setAccountsTableState,
|
||||
|
||||
// #withAccounts
|
||||
accountsCurrentView
|
||||
}) {
|
||||
// Accounts chart context.
|
||||
const { resourceViews } = useAccountsChartContext();
|
||||
|
||||
// Handles the tab change.
|
||||
const handleTabChange = useCallback(
|
||||
(viewSlug) => {
|
||||
setAccountsTableState({
|
||||
viewSlug: viewSlug || null,
|
||||
});
|
||||
},
|
||||
[setAccountsTableState],
|
||||
);
|
||||
|
||||
// Transfromes the accounts views to tabs.
|
||||
const tabs = transfromViewsToTabs(resourceViews);
|
||||
|
||||
return (
|
||||
<Navbar className="navbar--dashboard-views">
|
||||
<NavbarGroup align={Alignment.LEFT}>
|
||||
<DashboardViewsTabs
|
||||
defaultTabText={intl.get('all_accounts_')}
|
||||
currentViewSlug={accountsCurrentView}
|
||||
resourceName={'accounts'}
|
||||
onChange={handleTabChange}
|
||||
tabs={tabs}
|
||||
/>
|
||||
</NavbarGroup>
|
||||
</Navbar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAccountsTableActions,
|
||||
withAccounts(({ accountsTableState }) => ({
|
||||
accountsCurrentView: accountsTableState.viewSlug
|
||||
}))
|
||||
)(AccountsViewsTabs);
|
||||
109
src/containers/Accounts/components.js
Normal file
109
src/containers/Accounts/components.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Position,
|
||||
Classes,
|
||||
Tooltip,
|
||||
MenuItem,
|
||||
Menu,
|
||||
MenuDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import { Icon, Money, If } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { safeCallback } from 'utils';
|
||||
|
||||
/**
|
||||
* Accounts table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: {
|
||||
onEdit,
|
||||
onViewDetails,
|
||||
onDelete,
|
||||
onNewChild,
|
||||
onActivate,
|
||||
onInactivate,
|
||||
// onDrawer,
|
||||
},
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="reader-18" />}
|
||||
text={intl.get('view_details')}
|
||||
onClick={safeCallback(onViewDetails, original)}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('edit_account')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={<Icon icon="plus" />}
|
||||
text={intl.get('new_child_account')}
|
||||
onClick={safeCallback(onNewChild, original)}
|
||||
/>
|
||||
<MenuDivider />
|
||||
<If condition={original.active}>
|
||||
<MenuItem
|
||||
text={intl.get('inactivate_account')}
|
||||
icon={<Icon icon="pause-16" iconSize={16} />}
|
||||
onClick={safeCallback(onInactivate, original)}
|
||||
/>
|
||||
</If>
|
||||
<If condition={!original.active}>
|
||||
<MenuItem
|
||||
text={intl.get('activate_account')}
|
||||
icon={<Icon icon="play-16" iconSize={16} />}
|
||||
onClick={safeCallback(onActivate, original)}
|
||||
/>
|
||||
</If>
|
||||
<MenuItem
|
||||
text={intl.get('delete_account')}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal cell.
|
||||
*/
|
||||
export function NormalCell({ cell: { value } }) {
|
||||
const arrowDirection = value === 'credit' ? 'down' : 'up';
|
||||
|
||||
// Can't continue if the value is not `credit` or `debit`.
|
||||
if (['credit', 'debit'].indexOf(value) === -1) {
|
||||
return '';
|
||||
}
|
||||
return (
|
||||
<Tooltip
|
||||
className={Classes.TOOLTIP_INDICATOR}
|
||||
content={intl.get(value)}
|
||||
position={Position.RIGHT}
|
||||
hoverOpenDelay={100}
|
||||
>
|
||||
<Icon icon={`arrow-${arrowDirection}`} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance cell.
|
||||
*/
|
||||
export function BalanceCell({ cell }) {
|
||||
const account = cell.row.original;
|
||||
|
||||
return account.amount !== null ? (
|
||||
<span>
|
||||
{account.formatted_amount}
|
||||
{/* <Money amount={account.amount} currency={account.currency_code} /> */}
|
||||
</span>
|
||||
) : (
|
||||
<span class="placeholder">—</span>
|
||||
);
|
||||
}
|
||||
124
src/containers/Accounts/utils.js
Normal file
124
src/containers/Accounts/utils.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import React from 'react';
|
||||
import { Intent, Tag } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import { CLASSES } from '../../common/classes';
|
||||
import { If, AppToaster } from 'components';
|
||||
import { NormalCell, BalanceCell } from './components';
|
||||
import { transformTableStateToQuery, isBlank } from 'utils';
|
||||
|
||||
/**
|
||||
* Account name accessor.
|
||||
*/
|
||||
export const accountNameAccessor = (account) => {
|
||||
return (
|
||||
<span>
|
||||
<span class={'account-name'}>{account.name}</span>
|
||||
<If condition={account.description}>
|
||||
<span class={'account-desc'}>{account.description}</span>
|
||||
</If>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle delete errors in bulk and singular.
|
||||
*/
|
||||
export const handleDeleteErrors = (errors) => {
|
||||
if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('you_could_not_delete_predefined_accounts'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('cannot_delete_account_has_associated_transactions'),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const AccountCodeAccessor = (row) =>
|
||||
!isBlank(row.code) ? (
|
||||
<Tag minimal={true} round={true} intent={Intent.NONE}>
|
||||
{row.code}
|
||||
</Tag>
|
||||
) : null;
|
||||
|
||||
/**
|
||||
* Accounts table columns.
|
||||
*/
|
||||
export const useAccountsTableColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'name',
|
||||
Header: intl.get('account_name'),
|
||||
accessor: 'name',
|
||||
className: 'account_name',
|
||||
width: 200,
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'code',
|
||||
Header: intl.get('code'),
|
||||
accessor: AccountCodeAccessor,
|
||||
className: 'code',
|
||||
width: 80,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
Header: intl.get('type'),
|
||||
accessor: 'account_type_label',
|
||||
className: 'type',
|
||||
width: 140,
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'normal',
|
||||
Header: intl.get('account_normal'),
|
||||
Cell: NormalCell,
|
||||
accessor: 'account_normal',
|
||||
className: 'normal',
|
||||
width: 80,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'currency',
|
||||
Header: intl.get('currency'),
|
||||
accessor: 'currency_code',
|
||||
width: 75,
|
||||
clickable: true,
|
||||
},
|
||||
{
|
||||
id: 'balance',
|
||||
Header: intl.get('balance'),
|
||||
accessor: 'amount',
|
||||
Cell: BalanceCell,
|
||||
width: 150,
|
||||
clickable: true,
|
||||
align: 'right',
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
export const rowClassNames = (row) => ({
|
||||
inactive: !row.original.active,
|
||||
});
|
||||
|
||||
/**
|
||||
* Transformes the table state to list query.
|
||||
*/
|
||||
export const transformAccountsStateToQuery = (tableState) => {
|
||||
return {
|
||||
...transformTableStateToQuery(tableState),
|
||||
inactive_mode: tableState.inactiveMode,
|
||||
}
|
||||
}
|
||||
20
src/containers/Accounts/withAccounts.js
Normal file
20
src/containers/Accounts/withAccounts.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getAccountsTableStateFactory,
|
||||
accountsTableStateChangedFactory,
|
||||
} from 'store/accounts/accounts.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const getAccountsTableState = getAccountsTableStateFactory();
|
||||
const accountsTableStateChanged = accountsTableStateChangedFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const mapped = {
|
||||
accountsTableState: getAccountsTableState(state, props),
|
||||
accountsTableStateChanged: accountsTableStateChanged(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
12
src/containers/Accounts/withAccountsTableActions.js
Normal file
12
src/containers/Accounts/withAccountsTableActions.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
setAccountsTableState,
|
||||
resetAccountsTableState,
|
||||
} from 'store/accounts/accounts.actions';
|
||||
|
||||
const mapActionsToProps = (dispatch) => ({
|
||||
setAccountsTableState: (queries) => dispatch(setAccountsTableState(queries)),
|
||||
resetAccountsTableState: () => dispatch(resetAccountsTableState()),
|
||||
});
|
||||
|
||||
export default connect(null, mapActionsToProps);
|
||||
13
src/containers/Alert/withAlertActions.js
Normal file
13
src/containers/Alert/withAlertActions.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { connect } from 'react-redux';
|
||||
import t from 'store/types';
|
||||
|
||||
export const mapStateToProps = (state, props) => {
|
||||
return {};
|
||||
};
|
||||
|
||||
export const mapDispatchToProps = (dispatch) => ({
|
||||
openAlert: (name, payload) => dispatch({ type: t.OPEN_ALERT, name, payload }),
|
||||
closeAlert: (name, payload) => dispatch({ type: t.CLOSE_ALERT, name, payload }),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps);
|
||||
19
src/containers/Alert/withAlertStoreConnect.js
Normal file
19
src/containers/Alert/withAlertStoreConnect.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
isAlertOpenFactory,
|
||||
getAlertPayloadFactory,
|
||||
} from 'store/dashboard/dashboard.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const isAlertOpen = isAlertOpenFactory();
|
||||
const getAlertPayload = getAlertPayloadFactory();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const mapped = {
|
||||
isOpen: isAlertOpen(state, props),
|
||||
payload: getAlertPayload(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
}
|
||||
67
src/containers/Alerts/AccountActivateAlert.js
Normal file
67
src/containers/Alerts/AccountActivateAlert.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster, FormattedMessage as T } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { useActivateAccount } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Account activate alert.
|
||||
*/
|
||||
function AccountActivateAlert({
|
||||
name,
|
||||
isOpen,
|
||||
payload: { accountId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const {
|
||||
mutateAsync: activateAccount,
|
||||
isLoading
|
||||
} = useActivateAccount();
|
||||
|
||||
// Handle alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert('account-activate');
|
||||
};
|
||||
|
||||
// Handle activate account confirm.
|
||||
const handleConfirmAccountActivate = () => {
|
||||
activateAccount(accountId).then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_account_has_been_successfully_activated'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert('account-activate');
|
||||
}).finally(() => {
|
||||
closeAlert('account-activate');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'activate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirmAccountActivate}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_activate_this_account'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(AccountActivateAlert);
|
||||
70
src/containers/Alerts/AccountBulkActivateAlert.js
Normal file
70
src/containers/Alerts/AccountBulkActivateAlert.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React, { useState } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { FormattedMessage as T, AppToaster } from 'components';
|
||||
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function AccountBulkActivateAlert({
|
||||
name,
|
||||
isOpen,
|
||||
payload: { accountsIds },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
requestBulkActivateAccounts,
|
||||
}) {
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const selectedRowsCount = 0;
|
||||
|
||||
// Handle alert cancel.
|
||||
const handleClose = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle Bulk activate account confirm.
|
||||
const handleConfirmBulkActivate = () => {
|
||||
setLoading(true);
|
||||
requestBulkActivateAccounts(accountsIds)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_accounts_has_been_successfully_activated'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('accounts-table');
|
||||
})
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={`${intl.get('activate')} (${selectedRowsCount})`}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleClose}
|
||||
onConfirm={handleConfirmBulkActivate}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_activate_this_accounts'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withAccountsActions,
|
||||
)(AccountBulkActivateAlert);
|
||||
83
src/containers/Alerts/AccountBulkDeleteAlert.js
Normal file
83
src/containers/Alerts/AccountBulkDeleteAlert.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import React, { useState } from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { handleDeleteErrors } from 'containers/Accounts/utils';
|
||||
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Account bulk delete alert.
|
||||
*/
|
||||
function AccountBulkDeleteAlert({
|
||||
// #ownProps
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { accountsIds },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withAccountsActions
|
||||
requestDeleteBulkAccounts,
|
||||
}) {
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
const selectedRowsCount = 0;
|
||||
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
// Handle confirm accounts bulk delete.
|
||||
const handleConfirmBulkDelete = () => {
|
||||
setLoading(true);
|
||||
requestDeleteBulkAccounts(accountsIds)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_accounts_has_been_successfully_deleted'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('accounts-table');
|
||||
})
|
||||
.catch((errors) => {
|
||||
handleDeleteErrors(errors);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={`${intl.get('delete')} (${selectedRowsCount})`}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirmBulkDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_these_accounts_you_will_not_able_restore_them'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withAccountsActions,
|
||||
)(AccountBulkDeleteAlert);
|
||||
71
src/containers/Alerts/AccountBulkInactivateAlert.js
Normal file
71
src/containers/Alerts/AccountBulkInactivateAlert.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import React, { useState } from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
function AccountBulkInactivateAlert({
|
||||
name,
|
||||
isOpen,
|
||||
payload: { accountsIds },
|
||||
|
||||
// #withAccountsActions
|
||||
requestBulkInactiveAccounts,
|
||||
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const selectedRowsCount = 0;
|
||||
|
||||
// Handle alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
// Handle Bulk Inactive accounts confirm.
|
||||
const handleConfirmBulkInactive = () => {
|
||||
setLoading(true);
|
||||
requestBulkInactiveAccounts(accountsIds)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_accounts_have_been_successfully_inactivated'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('accounts-table');
|
||||
})
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={`${intl.get('inactivate')} (${selectedRowsCount})`}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirmBulkInactive}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_inactive_this_accounts'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withAccountsActions,
|
||||
)(AccountBulkInactivateAlert);
|
||||
86
src/containers/Alerts/AccountDeleteAlert.js
Normal file
86
src/containers/Alerts/AccountDeleteAlert.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { handleDeleteErrors } from 'containers/Accounts/utils';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { useDeleteAccount } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Account delete alerts.
|
||||
*/
|
||||
function AccountDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { accountId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { isLoading, mutateAsync: deleteAccount } = useDeleteAccount();
|
||||
|
||||
// handle cancel delete account alert.
|
||||
const handleCancelAccountDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
// Handle confirm account delete.
|
||||
const handleConfirmAccountDelete = () => {
|
||||
deleteAccount(accountId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_account_has_been_successfully_deleted'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
closeDrawer('account-drawer');
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
handleDeleteErrors(errors);
|
||||
closeAlert(name);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelAccountDelete}
|
||||
onConfirm={handleConfirmAccountDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_account_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(AccountDeleteAlert);
|
||||
69
src/containers/Alerts/AccountInactivateAlert.js
Normal file
69
src/containers/Alerts/AccountInactivateAlert.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { useInactivateAccount } from 'hooks/query';
|
||||
|
||||
/**
|
||||
* Account inactivate alert.
|
||||
*/
|
||||
function AccountInactivateAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { accountId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const {
|
||||
mutateAsync: inactivateAccount,
|
||||
isLoading
|
||||
} = useInactivateAccount();
|
||||
|
||||
const handleCancelInactiveAccount = () => {
|
||||
closeAlert('account-inactivate');
|
||||
};
|
||||
|
||||
const handleConfirmAccountActive = () => {
|
||||
inactivateAccount(accountId).then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_account_has_been_successfully_inactivated'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
}).catch(() => {
|
||||
|
||||
}).finally(() => {
|
||||
closeAlert('account-inactivate');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'inactivate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelInactiveAccount}
|
||||
onConfirm={handleConfirmAccountActive}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_inactive_this_account'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(AccountInactivateAlert);
|
||||
85
src/containers/Alerts/Bills/BillDeleteAlert.js
Normal file
85
src/containers/Alerts/Bills/BillDeleteAlert.js
Normal file
@@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { handleDeleteErrors } from 'containers/Purchases/Bills/BillForm/utils';
|
||||
import { useDeleteBill } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Bill delete alert.
|
||||
*/
|
||||
function BillDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { billId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { isLoading, mutateAsync: deleteBillMutate } = useDeleteBill();
|
||||
|
||||
// Handle cancel Bill
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete invoice
|
||||
const handleConfirmBillDelete = () => {
|
||||
deleteBillMutate(billId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_bill_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
|
||||
closeDrawer('bill-drawer');
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
handleDeleteErrors(errors);
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon={'trash'}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirmBillDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_this_bill_you_will_able_to_restore_it'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(BillDeleteAlert);
|
||||
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { useDeleteLandedCost } from 'hooks/query';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Bill transaction delete alert.
|
||||
*/
|
||||
function BillTransactionDeleteAlert({
|
||||
name,
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { BillId },
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: deleteLandedCostMutate, isLoading } =
|
||||
useDeleteLandedCost();
|
||||
|
||||
// Handle cancel delete.
|
||||
const handleCancelAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete .
|
||||
const handleConfirmLandedCostDelete = () => {
|
||||
deleteLandedCostMutate(BillId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_landed_cost_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelAlert}
|
||||
onConfirm={handleConfirmLandedCostDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p><T id={`Once your delete this located landed cost, you won't be able to restore it later, Are your sure you want to delete this transaction?`}/></p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(BillTransactionDeleteAlert);
|
||||
69
src/containers/Alerts/Bills/BillOpenAlert.js
Normal file
69
src/containers/Alerts/Bills/BillOpenAlert.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { useOpenBill } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Bill open alert.
|
||||
*/
|
||||
function BillOpenAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { billId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { isLoading, mutateAsync: openBillMutate } = useOpenBill();
|
||||
|
||||
// Handle cancel open bill alert.
|
||||
const handleCancelOpenBill = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm bill open.
|
||||
const handleConfirmBillOpen = () => {
|
||||
openBillMutate(billId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_bill_has_been_opened_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch((error) => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'open'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelOpenBill}
|
||||
onConfirm={handleConfirmBillOpen}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_open_this_bill'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(BillOpenAlert);
|
||||
67
src/containers/Alerts/Contacts/ContactActivateAlert.js
Normal file
67
src/containers/Alerts/Contacts/ContactActivateAlert.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { useActivateContact } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Contact activate alert.
|
||||
*/
|
||||
function ContactActivateAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { contactId, service },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: activateContact, isLoading } = useActivateContact();
|
||||
|
||||
// Handle activate contact alert cancel.
|
||||
const handleCancelActivateContact = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm contact activated.
|
||||
const handleConfirmContactActivate = () => {
|
||||
activateContact(contactId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_contact_has_been_activated_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'activate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelActivateContact}
|
||||
loading={isLoading}
|
||||
onConfirm={handleConfirmContactActivate}
|
||||
>
|
||||
<p>{intl.get('are_sure_to_activate_this_contact')}</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ContactActivateAlert);
|
||||
70
src/containers/Alerts/Contacts/ContactInactivateAlert.js
Normal file
70
src/containers/Alerts/Contacts/ContactInactivateAlert.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { useInactivateContact } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Contact inactivate alert.
|
||||
*/
|
||||
function ContactInactivateAlert({
|
||||
name,
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { contactId, service },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: inactivateContact, isLoading } = useInactivateContact();
|
||||
|
||||
// Handle cancel inactivate alert.
|
||||
const handleCancelInactivateContact = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm contact Inactive.
|
||||
const handleConfirmContactInactive = () => {
|
||||
inactivateContact(contactId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_contact_has_been_inactivated_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'inactivate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelInactivateContact}
|
||||
onConfirm={handleConfirmContactInactive}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
{intl.get('are_sure_to_inactive_this_contact', {
|
||||
name: service,
|
||||
})}
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ContactInactivateAlert);
|
||||
77
src/containers/Alerts/Currencies/CurrencyDeleteAlert.js
Normal file
77
src/containers/Alerts/Currencies/CurrencyDeleteAlert.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { useDeleteCurrency } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Currency delete alerts.
|
||||
*/
|
||||
function CurrencyDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { currency_code },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: deleteCurrency, isLoading } = useDeleteCurrency();
|
||||
|
||||
// handle cancel delete currency alert.
|
||||
const handleCancelCurrencyDelete = () => closeAlert(name);
|
||||
|
||||
// handle alert confirm delete currency.
|
||||
const handleConfirmCurrencyDelete = () => {
|
||||
deleteCurrency(currency_code)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_currency_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
if (errors.find(e => e.type === 'CANNOT_DELETE_BASE_CURRENCY')) {
|
||||
AppToaster.show({
|
||||
intent: Intent.DANGER,
|
||||
message: 'Cannot delete the base currency.'
|
||||
});
|
||||
}
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelCurrencyDelete}
|
||||
onConfirm={handleConfirmCurrencyDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_currency_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(CurrencyDeleteAlert);
|
||||
76
src/containers/Alerts/Customers/CustomerBulkDeleteAlert.js
Normal file
76
src/containers/Alerts/Customers/CustomerBulkDeleteAlert.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { transformErrors } from 'containers/Customers/utils';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Customer bulk delete alert.
|
||||
*/
|
||||
function CustomerBulkDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { customersIds },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
// handle cancel delete alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
console.log(customersIds, 'EE');
|
||||
|
||||
// Handle confirm customers bulk delete.
|
||||
const handleConfirmBulkDelete = useCallback(() => {
|
||||
setLoading(true);
|
||||
requestDeleteBulkCustomers(customersIds)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_customers_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((errors) => {
|
||||
transformErrors(errors);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
});
|
||||
}, [requestDeleteBulkCustomers, customersIds]);
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmBulkDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_these_customers_you_will_not_able_restore_them'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(CustomerBulkDeleteAlert);
|
||||
79
src/containers/Alerts/Customers/CustomerDeleteAlert.js
Normal file
79
src/containers/Alerts/Customers/CustomerDeleteAlert.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { transformErrors } from 'containers/Customers/utils';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { useDeleteCustomer } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
/**
|
||||
* Customer delete alert.
|
||||
*/
|
||||
function CustomerDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { contactId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const {
|
||||
mutateAsync: deleteCustomerMutate,
|
||||
isLoading
|
||||
} = useDeleteCustomer();
|
||||
|
||||
// handle cancel delete alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// handle confirm delete customer.
|
||||
const handleConfirmDeleteCustomer = useCallback(() => {
|
||||
deleteCustomerMutate(contactId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_customer_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
transformErrors(errors);
|
||||
})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
}, [deleteCustomerMutate, contactId, closeAlert, name]);
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmDeleteCustomer}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_customer_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(CustomerDeleteAlert);
|
||||
74
src/containers/Alerts/Estimates/EstimateApproveAlert.js
Normal file
74
src/containers/Alerts/Estimates/EstimateApproveAlert.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
|
||||
import { useApproveEstimate } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Estimate approve alert.
|
||||
*/
|
||||
function EstimateApproveAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { estimateId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const {
|
||||
mutateAsync: deliverEstimateMutate,
|
||||
isLoading,
|
||||
} = useApproveEstimate();
|
||||
|
||||
// handle cancel approve alert.
|
||||
const handleCancelApproveEstimate = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
// Handle confirm estimate approve.
|
||||
const handleConfirmEstimateApprove = useCallback(() => {
|
||||
deliverEstimateMutate(estimateId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_estimate_has_been_approved_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('estimates-table');
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
}, [estimateId, deliverEstimateMutate, closeAlert, name]);
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'approve'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
loading={isLoading}
|
||||
onCancel={handleCancelApproveEstimate}
|
||||
onConfirm={handleConfirmEstimateApprove}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_approve_this_estimate'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(EstimateApproveAlert);
|
||||
79
src/containers/Alerts/Estimates/EstimateDeleteAlert.js
Normal file
79
src/containers/Alerts/Estimates/EstimateDeleteAlert.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useDeleteEstimate } from 'hooks/query';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Estimate delete alert.
|
||||
*/
|
||||
function EstimateDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { estimateId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deleteEstimateMutate, isLoading } = useDeleteEstimate();
|
||||
|
||||
// handle cancel delete alert.
|
||||
const handleAlertCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// handle confirm delete estimate
|
||||
const handleAlertConfirm = () => {
|
||||
deleteEstimateMutate(estimateId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_estimate_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('estimate-detail-drawer');
|
||||
})
|
||||
.catch(({ errors }) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
loading={isLoading}
|
||||
onCancel={handleAlertCancel}
|
||||
onConfirm={handleAlertConfirm}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_estimate_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(EstimateDeleteAlert);
|
||||
70
src/containers/Alerts/Estimates/EstimateDeliveredAlert.js
Normal file
70
src/containers/Alerts/Estimates/EstimateDeliveredAlert.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useDeliverEstimate } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Estimate delivered alert.
|
||||
*/
|
||||
function EstimateDeliveredAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { estimateId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: deliverEstimateMutate, isLoading } = useDeliverEstimate();
|
||||
|
||||
// Handle cancel delivered estimate alert.
|
||||
const handleAlertCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm estimate delivered.
|
||||
const handleAlertConfirm = () => {
|
||||
deliverEstimateMutate(estimateId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_estimate_has_been_delivered_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
})
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'deliver'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleAlertCancel}
|
||||
onConfirm={handleAlertConfirm}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_deliver_this_estimate'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(EstimateDeliveredAlert);
|
||||
73
src/containers/Alerts/Estimates/EstimateRejectAlert.js
Normal file
73
src/containers/Alerts/Estimates/EstimateRejectAlert.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
import { useRejectEstimate } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Estimate reject delete alerts.
|
||||
*/
|
||||
function EstimateRejectAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { estimateId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const {
|
||||
mutateAsync: rejectEstimateMutate,
|
||||
isLoading
|
||||
} = useRejectEstimate();
|
||||
|
||||
// Handle cancel reject estimate alert.
|
||||
const handleCancelRejectEstimate = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm estimate reject.
|
||||
const handleConfirmEstimateReject = () => {
|
||||
rejectEstimateMutate(estimateId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_estimate_has_been_rejected_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'reject'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelRejectEstimate}
|
||||
onConfirm={handleConfirmEstimateReject}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_approve_this_estimate'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(EstimateRejectAlert);
|
||||
@@ -0,0 +1,72 @@
|
||||
import React, { useState } from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { size } from 'lodash';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Exchange rate bulk delete alert.
|
||||
*/
|
||||
function ExchangeRateBulkDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { exchangeRatesIds },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
// handle cancel item bulk delete alert.
|
||||
const handleCancelBulkDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// handle confirm Exchange Rates bulk delete.
|
||||
// const handleConfirmBulkDelete = () => {
|
||||
// bulkDeleteExchangeRate(exchangeRatesIds)
|
||||
// .then(() => {
|
||||
// AppToaster.show({
|
||||
// message: formatMessage({
|
||||
// id: 'the_exchange_rates_has_been_successfully_deleted',
|
||||
// }),
|
||||
// intent: Intent.SUCCESS,
|
||||
// });
|
||||
// })
|
||||
// .catch(({ errors }) => {
|
||||
// handleDeleteErrors(errors);
|
||||
// });
|
||||
// };
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={
|
||||
<T id={'delete_count'} values={{ count: size(exchangeRatesIds) }} />
|
||||
}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelBulkDelete}
|
||||
// onConfirm={}
|
||||
// loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T
|
||||
id={'once_delete_these_exchange_rates_you_will_not_able_restore_them'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ExchangeRateBulkDeleteAlert);
|
||||
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { useDeleteExchangeRate } from 'hooks/query';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* exchange rate delete alerts.
|
||||
*/
|
||||
function ExchangeRateDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { exchangeRateId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const {
|
||||
mutateAsync: deleteExchangeRate,
|
||||
isLoading,
|
||||
} = useDeleteExchangeRate();
|
||||
|
||||
|
||||
// Handle cancel delete exchange rate alert.
|
||||
const handleCancelExchangeRateDelete = () => closeAlert(name);
|
||||
|
||||
const handelConfirmExchangeRateDelete = () => {
|
||||
deleteExchangeRate(exchangeRateId)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_exchange_rates_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelExchangeRateDelete}
|
||||
onConfirm={handelConfirmExchangeRateDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_exchange_rate_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ExchangeRateDeleteAlert);
|
||||
67
src/containers/Alerts/Expenses/ExpenseBulkDeleteAlert.js
Normal file
67
src/containers/Alerts/Expenses/ExpenseBulkDeleteAlert.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { usePublishExpense } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Expense bulk delete alert.
|
||||
*/
|
||||
function ExpenseBulkDeleteAlert({
|
||||
closeAlert,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
name,
|
||||
payload: { expenseId, selectedCount },
|
||||
isOpen,
|
||||
}) {
|
||||
// Handle confirm journals bulk delete.
|
||||
const handleConfirmBulkDelete = () => {
|
||||
// requestDeleteBulkExpenses(bulkDelete)
|
||||
// .then(() => {
|
||||
// AppToaster.show({
|
||||
// message: formatMessage(
|
||||
// { id: 'the_expenses_have_been_deleted_successfully' },
|
||||
// { count: selectedRowsCount },
|
||||
// ),
|
||||
// intent: Intent.SUCCESS,
|
||||
// });
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// });
|
||||
};
|
||||
|
||||
// Handle cancel bulk delete alert.
|
||||
const handleCancelBulkDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={
|
||||
<T id={'delete_count'} values={{ count: selectedCount }} />
|
||||
}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelBulkDelete}
|
||||
onConfirm={handleConfirmBulkDelete}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_these_expenses_you_will_not_able_restore_them'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ExpenseBulkDeleteAlert);
|
||||
92
src/containers/Alerts/Expenses/ExpenseDeleteAlert.js
Normal file
92
src/containers/Alerts/Expenses/ExpenseDeleteAlert.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { useDeleteExpense } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Expense delete alert.
|
||||
*/
|
||||
function ExpenseDeleteAlert({
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { expenseId },
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deleteExpenseMutate, isLoading } = useDeleteExpense();
|
||||
|
||||
// Handle cancel expense journal.
|
||||
const handleCancelExpenseDelete = () => {
|
||||
closeAlert('expense-delete');
|
||||
};
|
||||
|
||||
// Handle confirm delete expense.
|
||||
const handleConfirmExpenseDelete = () => {
|
||||
deleteExpenseMutate(expenseId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_expense_has_been_deleted_successfully', {
|
||||
number: expenseId,
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('expense-drawer');
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (
|
||||
errors.find((e) => e.type === 'EXPENSE_HAS_ASSOCIATED_LANDED_COST')
|
||||
) {
|
||||
AppToaster.show({
|
||||
intent: Intent.DANGER,
|
||||
message: intl.get(
|
||||
'couldn_t_delete_expense_transaction_has_associated_located_landed_cost_transaction',
|
||||
),
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert('expense-delete');
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelExpenseDelete}
|
||||
onConfirm={handleConfirmExpenseDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_this_expense_you_will_able_to_restore_it'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(ExpenseDeleteAlert);
|
||||
55
src/containers/Alerts/Expenses/ExpenseDeleteEntriesAlert.js
Normal file
55
src/containers/Alerts/Expenses/ExpenseDeleteEntriesAlert.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose, saveInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Alert description.
|
||||
*/
|
||||
function ExpenseDeleteEntriesAlert({
|
||||
name,
|
||||
onConfirm,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
// Handle the alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm the alert.
|
||||
const handleConfirm = (event) => {
|
||||
closeAlert(name);
|
||||
saveInvoke(onConfirm, event)
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'clear_all_lines'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
loading={false}
|
||||
>
|
||||
<p>
|
||||
Clearing the table lines will delete all expense amounts were applied, Is this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ExpenseDeleteEntriesAlert);
|
||||
66
src/containers/Alerts/Expenses/ExpensePublishAlert.js
Normal file
66
src/containers/Alerts/Expenses/ExpensePublishAlert.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { usePublishExpense } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Expense publish alert.
|
||||
*/
|
||||
function ExpensePublishAlert({
|
||||
closeAlert,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
name,
|
||||
payload: { expenseId },
|
||||
isOpen,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: publishExpenseMutate, isLoading } = usePublishExpense();
|
||||
|
||||
const handleCancelPublishExpense = () => {
|
||||
closeAlert('expense-publish');
|
||||
};
|
||||
|
||||
// Handle publish expense confirm.
|
||||
const handleConfirmPublishExpense = () => {
|
||||
publishExpenseMutate(expenseId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_expense_has_been_published'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name)
|
||||
})
|
||||
.catch((error) => {
|
||||
closeAlert(name)
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'publish'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelPublishExpense}
|
||||
onConfirm={handleConfirmPublishExpense}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_publish_this_expense'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ExpensePublishAlert);
|
||||
87
src/containers/Alerts/Invoices/InvoiceDeleteAlert.js
Normal file
87
src/containers/Alerts/Invoices/InvoiceDeleteAlert.js
Normal file
@@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { useDeleteInvoice } from 'hooks/query';
|
||||
|
||||
import { handleDeleteErrors } from 'containers/Sales/Invoices/InvoicesLanding/components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Invoice delete alert.
|
||||
*/
|
||||
function InvoiceDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { invoiceId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deleteInvoiceMutate, isLoading } = useDeleteInvoice();
|
||||
|
||||
// handle cancel delete invoice alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// handleConfirm delete invoice
|
||||
const handleConfirmInvoiceDelete = () => {
|
||||
deleteInvoiceMutate(invoiceId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_invoice_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('invoice-detail-drawer');
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
handleDeleteErrors(errors);
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmInvoiceDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_invoice_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(InvoiceDeleteAlert);
|
||||
73
src/containers/Alerts/Invoices/InvoiceDeliverAlert.js
Normal file
73
src/containers/Alerts/Invoices/InvoiceDeliverAlert.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useDeliverInvoice } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Sale invoice alert.
|
||||
*/
|
||||
function InvoiceDeliverAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { invoiceId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const {
|
||||
mutateAsync: deliverInvoiceMutate,
|
||||
isLoading
|
||||
} = useDeliverInvoice();
|
||||
|
||||
// handle cancel delete deliver alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm invoice deliver.
|
||||
const handleConfirmInvoiceDeliver = () => {
|
||||
deliverInvoiceMutate(invoiceId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_invoice_has_been_delivered_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'deliver'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmInvoiceDeliver}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_deliver_this_invoice'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(InvoiceDeliverAlert);
|
||||
@@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import { useDeleteInventoryAdjustment } from 'hooks/query';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Inventory Adjustment delete alerts.
|
||||
*/
|
||||
function InventoryAdjustmentDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { inventoryId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deleteInventoryAdjMutate, isLoading } =
|
||||
useDeleteInventoryAdjustment();
|
||||
|
||||
// handle cancel delete alert.
|
||||
const handleCancelInventoryAdjustmentDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle the confirm delete of the inventory adjustment transaction.
|
||||
const handleConfirmInventoryAdjustmentDelete = () => {
|
||||
deleteInventoryAdjMutate(inventoryId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_adjustment_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('inventory-adjustment-drawer');
|
||||
})
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelInventoryAdjustmentDelete}
|
||||
onConfirm={handleConfirmInventoryAdjustmentDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={
|
||||
'once_delete_this_inventory_a_adjustment_you_will_able_to_restore_it'
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(InventoryAdjustmentDeleteAlert);
|
||||
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { usePublishInventoryAdjustment } from 'hooks/query';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Inventory Adjustment publish alert.
|
||||
*/
|
||||
|
||||
function InventoryAdjustmentPublishAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { inventoryId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: publishInventoryAdjustmentMutate, isLoading } =
|
||||
usePublishInventoryAdjustment();
|
||||
|
||||
// Handle cancel inventory adjustment alert.
|
||||
const handleCancelPublish = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle publish inventory adjustment confirm.
|
||||
const handleConfirmPublish = () => {
|
||||
publishInventoryAdjustmentMutate(inventoryId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('inventory_adjustment.publish.success_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch((error) => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'publish'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelPublish}
|
||||
onConfirm={handleConfirmPublish}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'inventory_adjustment.publish.alert_message'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(InventoryAdjustmentPublishAlert);
|
||||
72
src/containers/Alerts/Items/ItemActivateAlert.js
Normal file
72
src/containers/Alerts/Items/ItemActivateAlert.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import {
|
||||
useActivateItem,
|
||||
} from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Item activate alert.
|
||||
*/
|
||||
function ItemActivateAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { itemId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: activateItem, isLoading } = useActivateItem();
|
||||
|
||||
// Handle activate item alert cancel.
|
||||
const handleCancelActivateItem = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm item activated.
|
||||
const handleConfirmItemActivate = () => {
|
||||
activateItem(itemId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_item_has_been_activated_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'activate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelActivateItem}
|
||||
loading={isLoading}
|
||||
onConfirm={handleConfirmItemActivate}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_activate_this_item'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ItemActivateAlert);
|
||||
77
src/containers/Alerts/Items/ItemBulkDeleteAlert.js
Normal file
77
src/containers/Alerts/Items/ItemBulkDeleteAlert.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import React, { useState } from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { size } from 'lodash';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withItemsActions from 'containers/Items/withItemsActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Item bulk delete alert.
|
||||
*/
|
||||
function ItemBulkDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { itemsIds },
|
||||
|
||||
// #withItemsActions
|
||||
requestDeleteBulkItems,
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
// handle cancel item bulk delete alert.
|
||||
const handleCancelBulkDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
// Handle confirm items bulk delete.
|
||||
const handleConfirmBulkDelete = () => {
|
||||
setLoading(true);
|
||||
requestDeleteBulkItems(itemsIds)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_items_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={
|
||||
<T id={'delete_count'} values={{ count: size(itemsIds) }} />
|
||||
}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelBulkDelete}
|
||||
onConfirm={handleConfirmBulkDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_these_items_you_will_not_able_restore_them'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withItemsActions,
|
||||
)(ItemBulkDeleteAlert);
|
||||
82
src/containers/Alerts/Items/ItemCategoryBulkDeleteAlert.js
Normal file
82
src/containers/Alerts/Items/ItemCategoryBulkDeleteAlert.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import React, { useState } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { size } from 'lodash';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Item category bulk delete alerts.
|
||||
*/
|
||||
function ItemCategoryBulkDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { itemCategoriesIds },
|
||||
|
||||
// #withItemCategoriesActions
|
||||
requestDeleteBulkItemCategories,
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
|
||||
// handle cancel bulk delete alert.
|
||||
const handleCancelBulkDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// handle confirm itemCategories bulk delete.
|
||||
const handleConfirmBulkDelete = () => {
|
||||
setLoading(true);
|
||||
requestDeleteBulkItemCategories(itemCategoriesIds)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_item_categories_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={
|
||||
<T id={'delete_count'} values={{ count: size(itemCategoriesIds) }} />
|
||||
}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelBulkDelete}
|
||||
onConfirm={handleConfirmBulkDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={
|
||||
'once_delete_these_item_categories_you_will_not_able_restore_them'
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withItemCategoriesActions,
|
||||
)(ItemCategoryBulkDeleteAlert);
|
||||
76
src/containers/Alerts/Items/ItemCategoryDeleteAlert.js
Normal file
76
src/containers/Alerts/Items/ItemCategoryDeleteAlert.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useDeleteItemCategory } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Item Category delete alerts.
|
||||
*/
|
||||
function ItemCategoryDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { itemCategoryId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const {
|
||||
mutateAsync: deleteItemCategory,
|
||||
isLoading,
|
||||
} = useDeleteItemCategory();
|
||||
|
||||
// handle cancel delete item category alert.
|
||||
const handleCancelItemCategoryDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle alert confirm delete item category.
|
||||
const handleConfirmItemDelete = () => {
|
||||
deleteItemCategory(itemCategoryId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_item_category_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelItemCategoryDelete}
|
||||
onConfirm={handleConfirmItemDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_item_category_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ItemCategoryDeleteAlert);
|
||||
94
src/containers/Alerts/Items/ItemDeleteAlert.js
Normal file
94
src/containers/Alerts/Items/ItemDeleteAlert.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { handleDeleteErrors } from 'containers/Items/utils';
|
||||
import { useDeleteItem } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withItemsActions from 'containers/Items/withItemsActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Item delete alerts.
|
||||
*/
|
||||
function ItemDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { itemId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withItemsActions
|
||||
setItemsTableState,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deleteItem, isLoading } = useDeleteItem();
|
||||
|
||||
// Handle cancel delete item alert.
|
||||
const handleCancelItemDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete item.
|
||||
const handleConfirmDeleteItem = () => {
|
||||
deleteItem(itemId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_item_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
// Reset to page number one.
|
||||
setItemsTableState({ page: 1 });
|
||||
closeDrawer('item-detail-drawer');
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
handleDeleteErrors(errors);
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelItemDelete}
|
||||
onConfirm={handleConfirmDeleteItem}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_item_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withItemsActions,
|
||||
withDrawerActions,
|
||||
)(ItemDeleteAlert);
|
||||
70
src/containers/Alerts/Items/ItemInactivateAlert.js
Normal file
70
src/containers/Alerts/Items/ItemInactivateAlert.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { useInactivateItem } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Item inactivate alert.
|
||||
*/
|
||||
function ItemInactivateAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { itemId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: inactivateItem, isLoading } = useInactivateItem();
|
||||
|
||||
// Handle cancel inactivate alert.
|
||||
const handleCancelInactivateItem = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm item Inactive.
|
||||
const handleConfirmItemInactive = () => {
|
||||
inactivateItem(itemId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_item_has_been_inactivated_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'inactivate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelInactivateItem}
|
||||
onConfirm={handleConfirmItemInactive}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_inactive_this_item'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ItemInactivateAlert);
|
||||
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose, saveInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Items entries table clear all lines alert.
|
||||
*/
|
||||
function ItemsEntriesDeleteAlert({
|
||||
name,
|
||||
onConfirm,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
// Handle the alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm the alert.
|
||||
const handleConfirm = (event) => {
|
||||
closeAlert(name);
|
||||
saveInvoke(onConfirm, event)
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'clear_all_lines'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
loading={false}
|
||||
>
|
||||
<p>
|
||||
Clearing the table lines will delete all quantities and rate were applied to the items, Is this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ItemsEntriesDeleteAlert);
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
|
||||
function JournalBulkDeleteAlert({}) {
|
||||
// Handle confirm journals bulk delete.
|
||||
const handleConfirmBulkDelete = useCallback(() => {
|
||||
requestDeleteBulkManualJournals(bulkDelete)
|
||||
.then(() => {
|
||||
setBulkDelete(false);
|
||||
AppToaster.show({
|
||||
message: formatMessage(
|
||||
{ id: 'the_journals_has_been_deleted_successfully' },
|
||||
{ count: selectedRowsCount },
|
||||
),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setBulkDelete(false);
|
||||
});
|
||||
}, [
|
||||
requestDeleteBulkManualJournals,
|
||||
bulkDelete,
|
||||
formatMessage,
|
||||
selectedRowsCount,
|
||||
]);
|
||||
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={
|
||||
<T id={'delete_count'} values={{ count: selectedRowsCount }} />
|
||||
}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={bulkDelete}
|
||||
onCancel={handleCancelBulkDelete}
|
||||
onConfirm={handleConfirmBulkDelete}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_these_journals_you_will_not_able_restore_them'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
78
src/containers/Alerts/ManualJournals/JournalDeleteAlert.js
Normal file
78
src/containers/Alerts/ManualJournals/JournalDeleteAlert.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { useDeleteJournal } from 'hooks/query';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Journal delete alert.
|
||||
*/
|
||||
function JournalDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { manualJournalId, journalNumber },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deleteJournalMutate, isLoading } = useDeleteJournal();
|
||||
|
||||
// Handle cancel delete manual journal.
|
||||
const handleCancelAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete manual journal.
|
||||
const handleConfirmManualJournalDelete = () => {
|
||||
deleteJournalMutate(manualJournalId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_journal_has_been_deleted_successfully', {
|
||||
number: journalNumber,
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
closeDrawer('journal-drawer');
|
||||
})
|
||||
.catch(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelAlert}
|
||||
onConfirm={handleConfirmManualJournalDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_this_journal_you_will_able_to_restore_it'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions
|
||||
)(JournalDeleteAlert);
|
||||
@@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose, saveInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
* Make journal delete entries alert.
|
||||
*/
|
||||
function JournalDeleteEntriesAlert({
|
||||
// #ownProps
|
||||
name,
|
||||
onConfirm,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
// Handle the alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete manual journal.
|
||||
const handleConfirm = (event) => {
|
||||
closeAlert(name);
|
||||
saveInvoke(onConfirm, event);
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'clear_all_lines'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
loading={false}
|
||||
>
|
||||
<p>
|
||||
Clearing the table lines will delete all credits and debits were applied, Is this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(JournalDeleteEntriesAlert);
|
||||
72
src/containers/Alerts/ManualJournals/JournalPublishAlert.js
Normal file
72
src/containers/Alerts/ManualJournals/JournalPublishAlert.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { usePublishJournal } from 'hooks/query';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Journal publish alert.
|
||||
*/
|
||||
function JournalPublishAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { manualJournalId, journalNumber },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: publishJournalMutate, isLoading } = usePublishJournal();
|
||||
|
||||
// Handle cancel manual journal alert.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle publish manual journal confirm.
|
||||
const handleConfirm = () => {
|
||||
publishJournalMutate(manualJournalId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_manual_journal_has_been_published'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'publish'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_publish_this_manual_journal'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(JournalPublishAlert)
|
||||
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { saveInvoke } from '../../../utils';
|
||||
|
||||
/**
|
||||
* Changing full-amount alert in payment made form.
|
||||
*/
|
||||
function ChangingFullAmountAlert({
|
||||
name,
|
||||
onConfirm,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
// Handle the alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete manual journal.
|
||||
const handleConfirm = (event) => {
|
||||
closeAlert(name);
|
||||
saveInvoke(onConfirm, event)
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
>
|
||||
<p>
|
||||
Changing full amount will change all credit and payment were applied, Is
|
||||
this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ChangingFullAmountAlert);
|
||||
54
src/containers/Alerts/PaymentMades/ClearTransactionAlert.js
Normal file
54
src/containers/Alerts/PaymentMades/ClearTransactionAlert.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Alert description.
|
||||
*/
|
||||
function ClearPaymentTransactionAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
// Handle the alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete manual journal.
|
||||
const handleConfirm = () => {
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'action'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
loading={false}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_you_sure_you_want_to_clear_this_transaction'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ClearPaymentTransactionAlert);
|
||||
54
src/containers/Alerts/PaymentMades/ClearningAllLinesAlert.js
Normal file
54
src/containers/Alerts/PaymentMades/ClearningAllLinesAlert.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Clearning all lines alert.
|
||||
*/
|
||||
function ClearAllLinesAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: {},
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
// Handle the alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete manual journal.
|
||||
const handleConfirm = () => {};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'action'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
loading={false}
|
||||
>
|
||||
<p>
|
||||
Clearing the table lines will delete all credits and payments were
|
||||
applied. Is this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ClearAllLinesAlert);
|
||||
74
src/containers/Alerts/PaymentMades/PaymentMadeDeleteAlert.js
Normal file
74
src/containers/Alerts/PaymentMades/PaymentMadeDeleteAlert.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { useDeletePaymentMade } from 'hooks/query';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Payment made delete alert.
|
||||
*/
|
||||
function PaymentMadeDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { paymentMadeId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deletePaymentMadeMutate, isLoading } =
|
||||
useDeletePaymentMade();
|
||||
|
||||
// Handle cancel payment made.
|
||||
const handleCancelPaymentMadeDelete = () => {};
|
||||
|
||||
// Handle confirm delete payment made
|
||||
const handleConfirmPaymentMadeDelete = () => {
|
||||
deletePaymentMadeMutate(paymentMadeId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_payment_made_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('payment-made-detail-drawer');
|
||||
})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon={'trash'}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelPaymentMadeDelete}
|
||||
onConfirm={handleConfirmPaymentMadeDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'once_delete_this_payment_made_you_will_able_to_restore_it'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(PaymentMadeDeleteAlert);
|
||||
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
|
||||
import { saveInvoke, compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Clearning all lines alert.
|
||||
*/
|
||||
function ClearningAllLinesAlert({
|
||||
name,
|
||||
onConfirm,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: {},
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
// Handle the alert cancel.
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete manual journal.
|
||||
const handleConfirm = (event) => {
|
||||
closeAlert(name);
|
||||
saveInvoke(onConfirm, event)
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'action'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirm}
|
||||
>
|
||||
<p>
|
||||
<T id={'clearing_the_table_lines_will_delete_all_credits'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ClearningAllLinesAlert);
|
||||
@@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useDeletePaymentReceive } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Payment receive delete alert.
|
||||
*/
|
||||
function PaymentReceiveDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { paymentReceiveId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deletePaymentReceiveMutate, isLoading } =
|
||||
useDeletePaymentReceive();
|
||||
|
||||
// Handle cancel payment Receive.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete payment receive.
|
||||
const handleConfirmPaymentReceiveDelete = () => {
|
||||
deletePaymentReceiveMutate(paymentReceiveId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get(
|
||||
'the_payment_receive_has_been_deleted_successfully',
|
||||
),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('payment-receive-detail-drawer');
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmPaymentReceiveDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_payment_receive_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(PaymentReceiveDeleteAlert);
|
||||
70
src/containers/Alerts/Receipts/ReceiptCloseAlert.js
Normal file
70
src/containers/Alerts/Receipts/ReceiptCloseAlert.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useCloseReceipt } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Receipt close alert.
|
||||
*/
|
||||
function ReceiptCloseAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { receiptId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: closeReceiptMutate, isLoading } = useCloseReceipt();
|
||||
|
||||
// handle cancel delete alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm receipt close.
|
||||
const handleConfirmReceiptClose = () => {
|
||||
closeReceiptMutate(receiptId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_receipt_has_been_closed_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'close'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmReceiptClose}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_close_this_receipt'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(ReceiptCloseAlert);
|
||||
78
src/containers/Alerts/Receipts/ReceiptDeleteAlert.js
Normal file
78
src/containers/Alerts/Receipts/ReceiptDeleteAlert.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { useDeleteReceipt } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Invoice alert.
|
||||
*/
|
||||
function NameDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { receiptId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const { mutateAsync: deleteReceiptMutate, isLoading } = useDeleteReceipt();
|
||||
|
||||
// Handle cancel delete alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete receipt
|
||||
const handleConfirmReceiptDelete = () => {
|
||||
deleteReceiptMutate(receiptId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_receipt_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('receipt-detail-drawer');
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmReceiptDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_receipt_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions
|
||||
)(NameDeleteAlert);
|
||||
68
src/containers/Alerts/Users/UserActivateAlert.js
Normal file
68
src/containers/Alerts/Users/UserActivateAlert.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Alert, Intent } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { useActivateUser } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* User inactivate alert.
|
||||
*/
|
||||
function UserActivateAlert({
|
||||
// #ownProps
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { userId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
|
||||
const { mutateAsync: userActivateMutate } = useActivateUser();
|
||||
|
||||
const handleConfirmActivate = () => {
|
||||
userActivateMutate(userId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_user_has_been_activated_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch((error) => {
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'activate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirmActivate}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_activate_this_account'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(UserActivateAlert);
|
||||
76
src/containers/Alerts/Users/UserDeleteAlert.js
Normal file
76
src/containers/Alerts/Users/UserDeleteAlert.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { useDeleteUser } from 'hooks/query';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'redux';
|
||||
|
||||
/**
|
||||
* User delete alert.
|
||||
*/
|
||||
function UserDeleteAlert({
|
||||
// #ownProps
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { userId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: deleteUserMutate, isLoading } = useDeleteUser();
|
||||
|
||||
const handleCancelUserDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
const handleConfirmUserDelete = () => {
|
||||
deleteUserMutate(userId)
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_user_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
if (errors.find(e => e.type === 'CANNOT_DELETE_LAST_USER')) {
|
||||
AppToaster.show({
|
||||
message: 'Cannot delete the last user in the system.',
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelUserDelete}
|
||||
onConfirm={handleConfirmUserDelete}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
Once you delete this user, you won't be able to restore it later. Are
|
||||
you sure you want to delete ?
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(UserDeleteAlert);
|
||||
74
src/containers/Alerts/Users/UserInactivateAlert.js
Normal file
74
src/containers/Alerts/Users/UserInactivateAlert.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Alert, Intent } from '@blueprintjs/core';
|
||||
import { AppToaster } from 'components';
|
||||
import { useInactivateUser } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* User inactivate alert.
|
||||
*/
|
||||
function UserInactivateAlert({
|
||||
// #ownProps
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { userId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
|
||||
const { mutateAsync: userInactivateMutate } = useInactivateUser();
|
||||
|
||||
const handleConfirmInactivate = () => {
|
||||
userInactivateMutate(userId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_user_has_been_inactivated_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
if (errors.find(e => e.type === 'CANNOT.TOGGLE.ACTIVATE.AUTHORIZED.USER')) {
|
||||
AppToaster.show({
|
||||
message: 'You could not activate/inactivate the same authorized user.',
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
}
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'inactivate'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirmInactivate}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_sure_to_inactive_this_account'} />
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(UserInactivateAlert);
|
||||
82
src/containers/Alerts/Vendors/VendorDeleteAlert.js
Normal file
82
src/containers/Alerts/Vendors/VendorDeleteAlert.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
import { transformErrors } from 'containers/Vendors/utils';
|
||||
import { useDeleteVendor } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Vendor delete alert.
|
||||
*/
|
||||
function VendorDeleteAlert({
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { contactId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: deleteVendorMutate, isLoading } = useDeleteVendor();
|
||||
|
||||
// Handle cancel delete the vendor.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete vendor.
|
||||
const handleConfirmDeleteVendor = useCallback(() => {
|
||||
deleteVendorMutate(contactId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('the_vendor_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
transformErrors(errors);
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
}, [deleteVendorMutate, name, closeAlert, contactId]);
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'delete'} />}
|
||||
icon="trash"
|
||||
intent={Intent.DANGER}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancelDeleteAlert}
|
||||
onConfirm={handleConfirmDeleteVendor}
|
||||
loading={isLoading}
|
||||
>
|
||||
<p>
|
||||
<FormattedHTMLMessage
|
||||
id={'once_delete_this_vendor_you_will_able_to_restore_it'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(VendorDeleteAlert);
|
||||
5
src/containers/Alerts/index.js
Normal file
5
src/containers/Alerts/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import AccountDeleteAlert from './AccountDeleteAlert';
|
||||
|
||||
export default {
|
||||
AccountDeleteAlert,
|
||||
};
|
||||
19
src/containers/Authentication/AuthCopyright.js
Normal file
19
src/containers/Authentication/AuthCopyright.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import Icon from 'components/Icon';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
export default function AuthCopyright() {
|
||||
return (
|
||||
<div class="auth-copyright">
|
||||
<div class="auth-copyright__text">
|
||||
{intl.get('all_rights_reserved', {
|
||||
pre: moment().subtract(1, 'years').year(),
|
||||
current: moment().get('year'),
|
||||
})}
|
||||
</div>
|
||||
|
||||
<Icon width={122} height={22} icon={'bigcapital'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
src/containers/Authentication/AuthInsider.js
Normal file
23
src/containers/Authentication/AuthInsider.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import AuthCopyright from './AuthCopyright';
|
||||
|
||||
/**
|
||||
* Authentication insider page.
|
||||
*/
|
||||
export default function AuthInsider({
|
||||
logo = true,
|
||||
copyright = true,
|
||||
children,
|
||||
}) {
|
||||
return (
|
||||
<div class="authentication-insider__content">
|
||||
<div class="authentication-insider__form">
|
||||
{ children }
|
||||
</div>
|
||||
|
||||
<div class="authentication-insider__footer">
|
||||
<AuthCopyright />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
15
src/containers/Authentication/AuthenticationBoot.js
Normal file
15
src/containers/Authentication/AuthenticationBoot.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
|
||||
import withDashboardActions from '../../containers/Dashboard/withDashboardActions';
|
||||
|
||||
function AuthenticationBootJSX({ setAppIsLoading }) {
|
||||
React.useEffect(() => {
|
||||
setAppIsLoading(false);
|
||||
}, [setAppIsLoading]);
|
||||
|
||||
return null;
|
||||
}
|
||||
export const AuthenticationBoot = R.compose(withDashboardActions)(
|
||||
AuthenticationBootJSX,
|
||||
);
|
||||
20
src/containers/Authentication/InviteAccept.js
Normal file
20
src/containers/Authentication/InviteAccept.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import InviteAcceptForm from './InviteAcceptForm';
|
||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||
import { InviteAcceptProvider } from './InviteAcceptProvider';
|
||||
|
||||
/**
|
||||
* Authentication invite page.
|
||||
*/
|
||||
export default function Invite() {
|
||||
const { token } = useParams();
|
||||
|
||||
return (
|
||||
<AuthInsider>
|
||||
<InviteAcceptProvider token={token}>
|
||||
<InviteAcceptForm />
|
||||
</InviteAcceptProvider>
|
||||
</AuthInsider>
|
||||
);
|
||||
}
|
||||
101
src/containers/Authentication/InviteAcceptForm.js
Normal file
101
src/containers/Authentication/InviteAcceptForm.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import React from 'react';
|
||||
import { Intent, Position } from '@blueprintjs/core';
|
||||
import { Formik } from 'formik';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import { useInviteAcceptContext } from './InviteAcceptProvider';
|
||||
import { AppToaster } from 'components';
|
||||
import { InviteAcceptSchema } from './utils';
|
||||
import InviteAcceptFormContent from './InviteAcceptFormContent';
|
||||
|
||||
export default function InviteAcceptForm() {
|
||||
const history = useHistory();
|
||||
|
||||
// Invite accept context.
|
||||
const { inviteAcceptMutate, inviteMeta, token } = useInviteAcceptContext();
|
||||
|
||||
// Invite value.
|
||||
const inviteValue = {
|
||||
organization_name: '',
|
||||
invited_email: '',
|
||||
...(!isEmpty(inviteMeta)
|
||||
? {
|
||||
invited_email: inviteMeta.email,
|
||||
organization_name: inviteMeta.organizationName,
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
// Handle form submitting.
|
||||
const handleSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
inviteAcceptMutate([values, token])
|
||||
.then((response) => {
|
||||
AppToaster.show({
|
||||
message: intl.getHTML(
|
||||
'congrats_your_account_has_been_created_and_invited',
|
||||
{
|
||||
organization_name: inviteValue.organization_name,
|
||||
},
|
||||
),
|
||||
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
setSubmitting(false);
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (errors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('an_unexpected_error_occurred'),
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
}
|
||||
if (errors.find((e) => e.type === 'PHONE_MUMNER.ALREADY.EXISTS')) {
|
||||
setErrors({
|
||||
phone_number: 'This phone number is used in another account.',
|
||||
});
|
||||
}
|
||||
if (errors.find((e) => e.type === 'INVITE.TOKEN.NOT.FOUND')) {
|
||||
AppToaster.show({
|
||||
message: intl.get('an_unexpected_error_occurred'),
|
||||
intent: Intent.DANGER,
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
history.push('/auth/login');
|
||||
}
|
||||
setSubmitting(false);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'invite-form'}>
|
||||
<div className={'authentication-page__label-section'}>
|
||||
<h3>
|
||||
<T id={'welcome_to_bigcapital'} />
|
||||
</h3>
|
||||
<p>
|
||||
<T id={'enter_your_personal_information'} />{' '}
|
||||
<b>{inviteValue.organization_name}</b> <T id={'organization'} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Formik
|
||||
validationSchema={InviteAcceptSchema}
|
||||
initialValues={inviteValue}
|
||||
onSubmit={handleSubmit}
|
||||
component={InviteAcceptFormContent}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
131
src/containers/Authentication/InviteAcceptFormContent.js
Normal file
131
src/containers/Authentication/InviteAcceptFormContent.js
Normal file
@@ -0,0 +1,131 @@
|
||||
import React from 'react';
|
||||
import { Button, InputGroup, Intent, FormGroup } from '@blueprintjs/core';
|
||||
import { Form, ErrorMessage, FastField, useFormikContext } from 'formik';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { inputIntent } from 'utils';
|
||||
import { Col, Row } from 'components';
|
||||
import { useInviteAcceptContext } from './InviteAcceptProvider';
|
||||
import { PasswordRevealer } from './components';
|
||||
/**
|
||||
* Invite user form.
|
||||
*/
|
||||
export default function InviteUserFormContent() {
|
||||
// Invite accept context.
|
||||
const { inviteMeta } = useInviteAcceptContext();
|
||||
|
||||
// Formik context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
const [passwordType, setPasswordType] = React.useState('password');
|
||||
|
||||
// Handle password revealer changing.
|
||||
const handlePasswordRevealerChange = React.useCallback(
|
||||
(shown) => {
|
||||
const type = shown ? 'text' : 'password';
|
||||
setPasswordType(type);
|
||||
},
|
||||
[setPasswordType],
|
||||
);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Row>
|
||||
<Col md={6}>
|
||||
<FastField name={'first_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'first_name'} />}
|
||||
className={'form-group--first_name'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'first_name'} />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<FastField name={'last_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'last_name'} />}
|
||||
className={'form-group--last_name'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'last_name'} />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FastField name={'phone_number'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'phone_number'} />}
|
||||
className={'form-group--phone_number'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'phone_number'} />}
|
||||
>
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<FastField name={'password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={
|
||||
<PasswordRevealer onChange={handlePasswordRevealerChange} />
|
||||
}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'password'} />}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
type={passwordType}
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
<div className={'invite-form__statement-section'}>
|
||||
<p>
|
||||
<T id={'you_email_address_is'} /> <b>{inviteMeta.email},</b> <br />
|
||||
<T id={'you_will_use_this_address_to_sign_in_to_bigcapital'} />
|
||||
</p>
|
||||
<p>
|
||||
{intl.getHTML('signing_in_or_creating', {
|
||||
terms: (msg) => <Link>{msg}</Link>,
|
||||
privacy: (msg) => <Link>{msg}</Link>,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
fill={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'create_account'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
56
src/containers/Authentication/InviteAcceptProvider.js
Normal file
56
src/containers/Authentication/InviteAcceptProvider.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { useInviteMetaByToken, useAuthInviteAccept } from 'hooks/query';
|
||||
import { InviteAcceptLoading } from './components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
const InviteAcceptContext = createContext();
|
||||
|
||||
/**
|
||||
* Invite accept provider.
|
||||
*/
|
||||
function InviteAcceptProvider({ token, ...props }) {
|
||||
// Invite meta by token.
|
||||
const {
|
||||
data: inviteMeta,
|
||||
error: inviteMetaError,
|
||||
isError: isInviteMetaError,
|
||||
isFetching: isInviteMetaLoading,
|
||||
} = useInviteMetaByToken(token, { retry: false });
|
||||
|
||||
// Invite accept mutate.
|
||||
const { mutateAsync: inviteAcceptMutate } = useAuthInviteAccept({
|
||||
retry: false,
|
||||
});
|
||||
|
||||
// History context.
|
||||
const history = useHistory();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (inviteMetaError) { history.push('/auth/login'); }
|
||||
}, [history, inviteMetaError]);
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
token,
|
||||
inviteMeta,
|
||||
inviteMetaError,
|
||||
isInviteMetaError,
|
||||
isInviteMetaLoading,
|
||||
inviteAcceptMutate
|
||||
};
|
||||
|
||||
if (inviteMetaError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<InviteAcceptLoading isLoading={isInviteMetaLoading}>
|
||||
{ isInviteMetaError }
|
||||
<InviteAcceptContext.Provider value={provider} {...props} />
|
||||
</InviteAcceptLoading>
|
||||
);
|
||||
}
|
||||
|
||||
const useInviteAcceptContext = () => useContext(InviteAcceptContext);
|
||||
|
||||
export { InviteAcceptProvider, useInviteAcceptContext };
|
||||
71
src/containers/Authentication/Login.js
Normal file
71
src/containers/Authentication/Login.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Formik } from 'formik';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import Toaster from 'components/AppToaster';
|
||||
import AuthInsider from 'containers/Authentication/AuthInsider';
|
||||
import { useAuthLogin } from 'hooks/query';
|
||||
|
||||
import LoginForm from './LoginForm';
|
||||
import { LoginSchema, transformLoginErrorsToToasts } from './utils';
|
||||
|
||||
/**
|
||||
* Login page.
|
||||
*/
|
||||
export default function Login() {
|
||||
const { mutateAsync: loginMutate } = useAuthLogin();
|
||||
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
loginMutate({
|
||||
crediential: values.crediential,
|
||||
password: values.password,
|
||||
}).catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
const toastBuilders = transformLoginErrorsToToasts(errors);
|
||||
|
||||
toastBuilders.forEach((builder) => {
|
||||
Toaster.show(builder);
|
||||
});
|
||||
setSubmitting(false);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AuthInsider>
|
||||
<div className="login-form">
|
||||
<div className={'authentication-page__label-section'}>
|
||||
<h3>
|
||||
<T id={'log_in'} />
|
||||
</h3>
|
||||
<T id={'need_bigcapital_account'} />
|
||||
<Link to="/auth/register">
|
||||
{' '}
|
||||
<T id={'create_an_account'} />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<Formik
|
||||
initialValues={{
|
||||
crediential: '',
|
||||
password: '',
|
||||
}}
|
||||
validationSchema={LoginSchema}
|
||||
onSubmit={handleSubmit}
|
||||
component={LoginForm}
|
||||
/>
|
||||
|
||||
<div class="authentication-page__footer-links">
|
||||
<Link to={'/auth/send_reset_password'}>
|
||||
<T id={'forget_my_password'} />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</AuthInsider>
|
||||
);
|
||||
}
|
||||
88
src/containers/Authentication/LoginForm.js
Normal file
88
src/containers/Authentication/LoginForm.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Checkbox,
|
||||
} from '@blueprintjs/core';
|
||||
import { Form, ErrorMessage, Field } from 'formik';
|
||||
import { T } from 'components';
|
||||
import { inputIntent } from 'utils';
|
||||
import { PasswordRevealer } from './components';
|
||||
|
||||
/**
|
||||
* Login form.
|
||||
*/
|
||||
export default function LoginForm({ isSubmitting }) {
|
||||
const [passwordType, setPasswordType] = React.useState('password');
|
||||
|
||||
// Handle password revealer changing.
|
||||
const handlePasswordRevealerChange = React.useCallback(
|
||||
(shown) => {
|
||||
const type = shown ? 'text' : 'password';
|
||||
setPasswordType(type);
|
||||
},
|
||||
[setPasswordType],
|
||||
);
|
||||
|
||||
return (
|
||||
<Form className={'authentication-page__form'}>
|
||||
<Field name={'crediential'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'email_or_phone_number'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'crediential'} />}
|
||||
className={'form-group--crediential'}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
large={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Field name={'password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={
|
||||
<PasswordRevealer onChange={handlePasswordRevealerChange} />
|
||||
}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'password'} />}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
>
|
||||
<InputGroup
|
||||
large={true}
|
||||
intent={inputIntent({ error, touched })}
|
||||
type={passwordType}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<div className={'login-form__checkbox-section'}>
|
||||
<Checkbox large={true} className={'checkbox--remember-me'}>
|
||||
<T id={'keep_me_logged_in'} />
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
<div className={'authentication-page__submit-button-wrap'}>
|
||||
<Button
|
||||
type={'submit'}
|
||||
intent={Intent.PRIMARY}
|
||||
fill={true}
|
||||
lang={true}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
<T id={'log_in'} />
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user