diff --git a/client/src/connectors/CurrencyFromDialog.connect.js b/client/src/connectors/CurrencyFromDialog.connect.js deleted file mode 100644 index 9d6c5632f..000000000 --- a/client/src/connectors/CurrencyFromDialog.connect.js +++ /dev/null @@ -1,35 +0,0 @@ -import { connect } from 'react-redux'; -import { - fetchCurrencies, - submitCurrencies, - deleteCurrency, - editCurrency, -} from 'store/currencies/currencies.actions'; -import { getDialogPayload } from 'store/dashboard/dashboard.reducer'; -import { getCurrencyById } from 'store/currencies/currencies.selector'; - -export const mapStateToProps = (state, props) => { - const dialogPayload = getDialogPayload(state, 'currency-form'); - - return { - currencies: state.currencies.preferences.currencies, - name: 'currency-form', - payload: { action: 'new', id: null, ...dialogPayload }, - editCurrency: - dialogPayload && dialogPayload.action === 'edit' - ? state.currencies.preferences.currencies[dialogPayload.currency_code] - : {}, - getCurrencyId: (id) => - getCurrencyById(state.currencies.preferences.currencies, id), - }; -}; - -export const mapDispatchToProps = (dispatch) => ({ - requestFetchCurrencies: () => dispatch(fetchCurrencies({})), - requestSubmitCurrencies: (form) => dispatch(submitCurrencies({ form })), - requestEditCurrency: (id, form) => dispatch(editCurrency({ id, form })), - requestDeleteCurrency: (currency_code) => - dispatch(deleteCurrency({ currency_code })), -}); - -export default connect(mapStateToProps, mapDispatchToProps); diff --git a/client/src/connectors/Journal.connect.js b/client/src/connectors/Journal.connect.js deleted file mode 100644 index 96325e56c..000000000 --- a/client/src/connectors/Journal.connect.js +++ /dev/null @@ -1,21 +0,0 @@ -import {connect} from 'react-redux'; -import { - fetchJournalSheet -} from 'store/financialStatement/financialStatements.actions'; -import { - getFinancialSheetIndexByQuery, - getFinancialSheet, -} from 'store/financialStatement/financialStatements.selectors'; - - -export const mapStateToProps = (state, props) => ({ - getJournalSheetIndex: (query) => getFinancialSheetIndexByQuery(state.financialStatements.journal.sheets, query), - getJournalSheet: (index) => getFinancialSheet(state.financialStatements.journal.sheets, index), - journalSheetLoading: state.financialStatements.journal.loading, -}); - -export const mapDispatchToProps = (dispatch) => ({ - fetchJournalSheet: (query) => dispatch(fetchJournalSheet({ query })), -}); - -export default connect(mapStateToProps, mapDispatchToProps); \ No newline at end of file diff --git a/client/src/connectors/MakeJournalEntries.connect.js b/client/src/connectors/MakeJournalEntries.connect.js deleted file mode 100644 index 645ef4e2a..000000000 --- a/client/src/connectors/MakeJournalEntries.connect.js +++ /dev/null @@ -1,21 +0,0 @@ -import {connect} from 'react-redux'; -import { - makeJournalEntries, - fetchManualJournal, - editManualJournal, -} from 'store/manualJournals/manualJournals.actions'; -import { - getManualJournal, -} from 'store/manualJournals/manualJournals.reducers'; - -export const mapStateToProps = (state, props) => ({ - getManualJournal: (id) => getManualJournal(state, id), -}); - -export const mapDispatchToProps = (dispatch) => ({ - requestMakeJournalEntries: (form) => dispatch(makeJournalEntries({ form })), - fetchManualJournal: (id) => dispatch(fetchManualJournal({ id })), - requestEditManualJournal: (id, form) => dispatch(editManualJournal({ id, form })) -}); - -export default connect(mapStateToProps, mapDispatchToProps); \ No newline at end of file diff --git a/client/src/containers/Accounting/MakeJournalEntriesForm.js b/client/src/containers/Accounting/MakeJournalEntriesForm.js index f477132a3..3754710f7 100644 --- a/client/src/containers/Accounting/MakeJournalEntriesForm.js +++ b/client/src/containers/Accounting/MakeJournalEntriesForm.js @@ -3,6 +3,7 @@ import * as Yup from 'yup'; import {useFormik} from "formik"; import moment from 'moment'; import { Intent } from '@blueprintjs/core'; +import { useIntl } from 'react-intl'; import MakeJournalEntriesHeader from './MakeJournalEntriesHeader'; import MakeJournalEntriesFooter from './MakeJournalEntriesFooter'; @@ -40,6 +41,7 @@ function MakeJournalEntriesForm({ onFormSubmit, onCancelForm, }) { + const { formatMessage } = useIntl(); const { setFiles, saveMedia, deletedFiles, setDeletedFiles, deleteMedia } = useMedia({ saveCallback: requestSubmitMedia, deleteCallback: requestDeleteMedia, @@ -159,7 +161,11 @@ function MakeJournalEntriesForm({ requestEditManualJournal(manualJournal.id, requestForm) .then((response) => { AppToaster.show({ - message: 'manual_journal_has_been_edited', + message: formatMessage({ + id: 'the_journal_has_been_successfully_edited', + }, { + number: values.journal_number, + }), intent: Intent.SUCCESS, }); setSubmitting(false); @@ -178,9 +184,13 @@ function MakeJournalEntriesForm({ requestMakeJournalEntries(requestForm) .then((response) => { AppToaster.show({ - message: 'manual_journal_has_been_submit', + message: formatMessage({ + id: 'the_journal_has_been_successfully_created', + }, { + number: values.journal_number, + }), intent: Intent.SUCCESS, - }); + }); setSubmitting(false); saveInvokeSubmit({ action: 'new', ...payload }); clearSavedMediaIds(); diff --git a/client/src/containers/Accounting/ManualJournalsDataTable.js b/client/src/containers/Accounting/ManualJournalsDataTable.js index 7a7c98b43..e09070226 100644 --- a/client/src/containers/Accounting/ManualJournalsDataTable.js +++ b/client/src/containers/Accounting/ManualJournalsDataTable.js @@ -2,7 +2,9 @@ import React, { useEffect, useCallback, useState, useMemo } from 'react'; import { Intent, Button, + Classes, Popover, + Tooltip, Menu, MenuItem, MenuDivider, @@ -25,6 +27,8 @@ import withViewDetails from 'containers/Views/withViewDetails'; import withManualJournals from 'containers/Accounting/withManualJournals'; import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions'; +import { If } from 'components'; + function ManualJournalsDataTable({ loading, @@ -135,7 +139,17 @@ function ManualJournalsDataTable({ { id: 'note', Header: 'Note', - accessor: r => (), + accessor: (row) => ( + + + + + + ), disableResizing: true, disableSorting: true, width: 100, diff --git a/client/src/containers/Accounts/AccountsChart.js b/client/src/containers/Accounts/AccountsChart.js index 7434704d1..f08815061 100644 --- a/client/src/containers/Accounts/AccountsChart.js +++ b/client/src/containers/Accounts/AccountsChart.js @@ -1,10 +1,11 @@ -import React, { useEffect, useState, useCallback } from 'react'; +import React, { useEffect, useState, useMemo, useCallback } from 'react'; import { Route, Switch, } from 'react-router-dom'; import { Alert, Intent } from '@blueprintjs/core'; import { useQuery } from 'react-query' +import { useIntl } from 'react-intl'; import AppToaster from 'components/AppToaster'; @@ -22,6 +23,7 @@ import withViewsActions from 'containers/Views/withViewsActions'; import withAccounts from 'containers/Accounts/withAccounts'; import { compose } from 'utils'; +import { FormattedMessage as T, FormattedHTMLMessage } from 'react-intl'; function AccountsChart({ @@ -32,6 +34,7 @@ function AccountsChart({ // #withAccountsActions requestDeleteAccount, requestInactiveAccount, + requestActivateAccount, // #withViewsActions requestFetchResourceViews, @@ -47,8 +50,11 @@ function AccountsChart({ // #withAccounts accountsTableQuery, }) { + const { formatMessage } = useIntl(); + const [deleteAccount, setDeleteAccount] = useState(false); const [inactiveAccount, setInactiveAccount] = useState(false); + const [activateAccount, setActivateAccount] = useState(false); const [bulkDelete, setBulkDelete] = useState(false); const [selectedRows, setSelectedRows] = useState([]); @@ -64,8 +70,7 @@ function AccountsChart({ // Fetch accounts list according to the given custom view id. const fetchAccountsHook = useQuery(['accounts-table', accountsTableQuery], - () => requestFetchAccountsTable(), - { refetchInterval: 3000 }); + () => requestFetchAccountsTable()); useEffect(() => { changePageTitle('Chart of Accounts'); @@ -76,25 +81,34 @@ function AccountsChart({ // handle cancel delete account alert. const handleCancelAccountDelete = useCallback(() => { setDeleteAccount(false); }, []); + + const handleDeleteErrors = (errors) => { + if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) { + AppToaster.show({ + message: formatMessage({ + id: 'you_could_not_delete_predefined_accounts', + }), + intent: Intent.DANGER, + }); + } + if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) { + AppToaster.show({ + message: 'cannot_delete_account_has_associated_transactions' + }); + } + } // Handle confirm account delete const handleConfirmAccountDelete = useCallback(() => { requestDeleteAccount(deleteAccount.id).then(() => { setDeleteAccount(false); - AppToaster.show({ message: 'the_account_has_been_deleted' }); + AppToaster.show({ + message: formatMessage({ id: 'the_account_has_been_successfully_deleted' }), + intent: Intent.SUCCESS, + }); }).catch(errors => { setDeleteAccount(false); - if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) { - AppToaster.show({ - message: 'cannot_delete_predefined_account', - intent: Intent.DANGER, - }); - } - if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) { - AppToaster.show({ - message: 'cannot_delete_account_has_associated_transactions' - }); - } + handleDeleteErrors(errors); }); }, [deleteAccount, requestDeleteAccount]); @@ -112,11 +126,34 @@ function AccountsChart({ const handleConfirmAccountActive = useCallback(() => { requestInactiveAccount(inactiveAccount.id).then(() => { setInactiveAccount(false); - requestFetchAccountsTable(); - AppToaster.show({ message: 'the_account_has_been_inactivated' }); + AppToaster.show({ + message: formatMessage({ + id: 'the_account_has_been_successfully_inactivated', + }), + intent: Intent.SUCCESS, + }); }); - }, [inactiveAccount, requestFetchAccountsTable, requestInactiveAccount]); + }, [inactiveAccount, requestInactiveAccount]); + const handleActivateAccount = useCallback((account) => { + setActivateAccount(account); + }); + + const handleCancelActivateAccount = useCallback(() => { + setActivateAccount(false); + }); + + const handleConfirmAccountActivate = useCallback(() => { + requestActivateAccount(activateAccount.id).then(() => { + setActivateAccount(false); + AppToaster.show({ + message: formatMessage({ + id: 'the_account_has_been_successfully_activated', + }), + intent: Intent.SUCCESS, + }); + }); + }); const handleEditAccount = (account) => { @@ -133,9 +170,13 @@ function AccountsChart({ const handleConfirmBulkDelete = useCallback(() => { requestDeleteBulkAccounts(bulkDelete).then(() => { setBulkDelete(false); - AppToaster.show({ message: 'the_accounts_have_been_deleted' }); - }).catch((error) => { + AppToaster.show({ + message: formatMessage({ id: 'the_accounts_has_been_successfully_deleted' }), + intent: Intent.SUCCESS, + }); + }).catch((errors) => { setBulkDelete(false); + handleDeleteErrors(errors); }); }, [requestDeleteBulkAccounts, bulkDelete]); @@ -179,6 +220,9 @@ function AccountsChart({ fetchAccountsHook.refetch(); }, [fetchAccountsHook, addAccountsTableQueries]); + // Calculates the data table selected rows count. + const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [selectedRows]); + return (

- Are you sure you want to move filename to Trash? You will be able to restore it later, - but it will become private to you. +

- Are you sure you want to move filename to Trash? You will be able to restore it later, - but it will become private to you. +

+

+ +

+
+ +

- Are you sure you want to move filename to Trash? You will be able to restore it later, - but it will become private to you. +

diff --git a/client/src/containers/Accounts/AccountsDataTable.js b/client/src/containers/Accounts/AccountsDataTable.js index 31f586528..e5017a13a 100644 --- a/client/src/containers/Accounts/AccountsDataTable.js +++ b/client/src/containers/Accounts/AccountsDataTable.js @@ -9,6 +9,8 @@ import { Classes, Tooltip, } from '@blueprintjs/core'; +import { useIntl } from 'react-intl'; + import Icon from 'components/Icon'; import { compose } from 'utils'; import DialogConnect from 'connectors/Dialog.connector'; @@ -21,6 +23,8 @@ import withDashboardActions from 'containers/Dashboard/withDashboard'; import withAccountsActions from 'containers/Accounts/withAccountsActions'; import withAccounts from 'containers/Accounts/withAccounts'; +import { If } from 'components'; + function AccountsDataTable({ // #withAccounts @@ -36,8 +40,10 @@ function AccountsDataTable({ onSelectedRowsChange, onDeleteAccount, onInactiveAccount, + onActivateAccount, }) { const [initialMount, setInitialMount] = useState(false); + const { formatMessage } = useIntl(); useUpdateEffect(() => { if (!accountsLoading) { @@ -64,9 +70,16 @@ function AccountsDataTable({ text='New Account' onClick={() => handleNewParentAccount(account)} /> - onInactiveAccount(account)} /> + + onInactiveAccount(account)} /> + + + onActivateAccount(account)} /> + onDeleteAccount(account)} /> @@ -109,10 +122,18 @@ function AccountsDataTable({ Header: 'Normal', Cell: ({ cell }) => { const account = cell.row.original; - const type = account.type ? account.type.normal : ''; - const arrowDirection = type === 'credit' ? 'down' : 'up'; + const normal = account.type ? account.type.normal : ''; + const arrowDirection = normal === 'credit' ? 'down' : 'up'; - return (); + return ( + + + + ); }, className: 'normal', width: 75, diff --git a/client/src/containers/Accounts/withAccountsActions.js b/client/src/containers/Accounts/withAccountsActions.js index 95b4b3be6..fcc4f0541 100644 --- a/client/src/containers/Accounts/withAccountsActions.js +++ b/client/src/containers/Accounts/withAccountsActions.js @@ -4,6 +4,7 @@ import { fetchAccountsList, deleteAccount, inactiveAccount, + activateAccount, submitAccount, fetchAccount, deleteBulkAccounts, @@ -15,6 +16,7 @@ const mapActionsToProps = (dispatch) => ({ requestSubmitAccount: ({ form }) => dispatch(submitAccount({ form })), requestDeleteAccount: (id) => dispatch(deleteAccount({ id })), requestInactiveAccount: (id) => dispatch(inactiveAccount({ id })), + requestActivateAccount: (id) => dispatch(activateAccount({ id })), requestFetchAccount: (id) => dispatch(fetchAccount({ id })), requestDeleteBulkAccounts: (ids) => dispatch(deleteBulkAccounts({ ids })), }); diff --git a/client/src/containers/Dialogs/AccountFormDialog.js b/client/src/containers/Dialogs/AccountFormDialog.js index c85594a42..1f1263a65 100644 --- a/client/src/containers/Dialogs/AccountFormDialog.js +++ b/client/src/containers/Dialogs/AccountFormDialog.js @@ -49,14 +49,14 @@ function AccountFormDialog({ // #withDialog closeDialog, -}) { - const intl = useIntl(); +}) { + const { formatMessage } = useIntl(); const accountFormValidationSchema = Yup.object().shape({ - name: Yup.string().required(intl.formatMessage({ id: 'required' })), + name: Yup.string().required(formatMessage({ id: 'required' })), code: Yup.number(), account_type_id: Yup.string() .nullable() - .required(intl.formatMessage({ id: 'required' })), + .required(formatMessage({ id: 'required' })), description: Yup.string().trim() }); @@ -89,6 +89,7 @@ function AccountFormDialog({ validationSchema: accountFormValidationSchema, onSubmit: (values, { setSubmitting, setErrors }) => { const exclude = ['subaccount']; + const toastAccountName = (values.code) ? `${values.code} - ${values.name}` : values.name; if (payload.action === 'edit') { requestEditAccount({ @@ -97,7 +98,12 @@ function AccountFormDialog({ }).then((response) => { closeDialog(name); AppToaster.show({ - message: 'the_account_has_been_edited', + message: formatMessage({ + id: 'service_has_been_successful_edited', + }, { + name: toastAccountName, + service: formatMessage({ id: 'account' }), + }), intent: Intent.SUCCESS, }); setSubmitting(false); @@ -110,7 +116,12 @@ function AccountFormDialog({ requestSubmitAccount({ form: { ...omit(values, exclude) } }).then((response) => { closeDialog(name); AppToaster.show({ - message: 'the_account_has_been_submit', + message: formatMessage({ + id: 'service_has_been_successful_created', + }, { + name: toastAccountName, + service: formatMessage({ id: 'account' }), + }), intent: Intent.SUCCESS, position: Position.BOTTOM, }); diff --git a/client/src/containers/Dialogs/ItemCategoryDialog.js b/client/src/containers/Dialogs/ItemCategoryDialog.js index 4af54e611..688a698f2 100644 --- a/client/src/containers/Dialogs/ItemCategoryDialog.js +++ b/client/src/containers/Dialogs/ItemCategoryDialog.js @@ -55,13 +55,13 @@ function ItemCategoryDialog({ requestEditItemCategory, }) { const [selectedParentCategory, setParentCategory] = useState(null); - const intl = useIntl(); + const { formatMessage } = useIntl(); const fetchList = useQuery(['items-categories-list'], () => requestFetchItemCategories()); - const ValidationSchema = Yup.object().shape({ - name: Yup.string().required(intl.formatMessage({ id: 'required' })), + const validationSchema = Yup.object().shape({ + name: Yup.string().required(), parent_category_id: Yup.string().nullable(), description: Yup.string().trim() }); @@ -88,13 +88,15 @@ function ItemCategoryDialog({ ...(payload.action === 'edit' && pick(itemCategory, Object.keys(initialValues))) }, - validationSchema: ValidationSchema, + validationSchema, onSubmit: (values, { setSubmitting }) => { if (payload.action === 'edit') { requestEditItemCategory(payload.id, values).then(response => { closeDialog(name); AppToaster.show({ - message: 'the_category_has_been_edited', + message: formatMessage({ + id: 'the_item_category_has_been_successfully_edited', + }), intent: Intent.SUCCESS, }); setSubmitting(false); @@ -107,7 +109,9 @@ function ItemCategoryDialog({ .then((response) => { closeDialog(name); AppToaster.show({ - message: 'the_category_has_been_submit', + message: formatMessage({ + id: 'the_item_category_has_been_successfully_created', + }), intent: Intent.SUCCESS, }); setSubmitting(false); diff --git a/client/src/containers/Dialogs/UserFormDialog.js b/client/src/containers/Dialogs/UserFormDialog.js index 695c0693c..9343b1860 100644 --- a/client/src/containers/Dialogs/UserFormDialog.js +++ b/client/src/containers/Dialogs/UserFormDialog.js @@ -23,13 +23,12 @@ import { compose } from 'utils'; function UserFormDialog({ requestFetchUser, requestSubmitInvite, - requestEditUser, name, payload, isOpen, closeDialog, }) { - const intl = useIntl(); + const { formatMessage } = useIntl(); const fetchHook = useAsync(async () => { await Promise.all([ ...(payload.action === 'edit' ? [requestFetchUser(payload.user.id)] : []), @@ -37,7 +36,7 @@ function UserFormDialog({ }, false); const validationSchema = Yup.object().shape({ - email: Yup.string().email().required(intl.formatMessage({id:'required'})), + email: Yup.string().email().required(), }); const initialValues = { @@ -56,29 +55,23 @@ function UserFormDialog({ resetForm, getFieldProps, handleSubmit, + isSubmitting, } = useFormik({ enableReinitialize: true, initialValues, validationSchema, onSubmit: (values) => { - const form = { - ...values, - }; - if (payload.action === 'edit') { - requestEditUser(payload.user.id, form).then((response) => { - AppToaster.show({ - message: 'the_user_details_has_been_updated', - }); - closeDialog(name); + const form = { ...values }; + + requestSubmitInvite(form).then((response) => { + AppToaster.show({ + message: formatMessage({ + id: 'teammate_invited_to_organization_account', + }), + intent: Intent.SUCCESS, }); - } else { - requestSubmitInvite(form).then((response) => { - AppToaster.show({ - message: 'the_user_has_been_invited', - }); - closeDialog(name); - }); - } + closeDialog(name); + }); }, }); const onDialogOpening = () => { @@ -89,9 +82,7 @@ function UserFormDialog({ resetForm(); }, [resetForm]); - const handleClose = () => { - closeDialog(name); - }; + const handleClose = () => { closeDialog(name); }; return (
-
diff --git a/client/src/containers/Items/ItemForm.js b/client/src/containers/Items/ItemForm.js index 1fc95ecef..c9631abd0 100644 --- a/client/src/containers/Items/ItemForm.js +++ b/client/src/containers/Items/ItemForm.js @@ -12,6 +12,7 @@ import { Checkbox, } from '@blueprintjs/core'; import { Row, Col } from 'react-grid-system'; +import { useIntl } from 'react-intl'; import { Select } from '@blueprintjs/select'; import AppToaster from 'components/AppToaster'; import AccountsConnect from 'connectors/Accounts.connector'; @@ -27,6 +28,7 @@ import Dragzone from 'components/Dragzone'; import MediaConnect from 'connectors/Media.connect'; import useMedia from 'hooks/useMedia'; + const ItemForm = ({ requestSubmitItem, @@ -38,6 +40,7 @@ const ItemForm = ({ }) => { const [selectedAccounts, setSelectedAccounts] = useState({}); const history = useHistory(); + const { formatMessage } = useIntl(); const { files, @@ -49,7 +52,7 @@ const ItemForm = ({ } = useMedia({ saveCallback: requestSubmitMedia, deleteCallback: requestDeleteMedia, - }) + }); const ItemTypeDisplay = useMemo(() => ([ { value: null, label: 'Select Item Type' }, @@ -97,6 +100,7 @@ const ItemForm = ({ touched, errors, handleSubmit, + isSubmitting, } = useFormik({ enableReinitialize: true, validationSchema: validationSchema, @@ -109,7 +113,13 @@ const ItemForm = ({ return requestSubmitItem(formValues).then((response) => { AppToaster.show({ - message: 'The_Items_has_been_submit' + message: formatMessage({ + id: 'service_has_been_successful_created', + }, { + name: values.name, + service: formatMessage({ id: 'item' }), + }), + intent: Intent.SUCCESS, }); setSubmitting(false); history.push('/dashboard/items'); @@ -439,11 +449,11 @@ const ItemForm = ({ diff --git a/client/src/containers/Items/ItemsList.js b/client/src/containers/Items/ItemsList.js index 7576f9d3a..f0604d928 100644 --- a/client/src/containers/Items/ItemsList.js +++ b/client/src/containers/Items/ItemsList.js @@ -8,6 +8,7 @@ import { Alert, } from '@blueprintjs/core'; import { useQuery } from 'react-query'; +import { FormattedHTMLMessage, useIntl } from 'react-intl'; import DashboardInsider from 'components/Dashboard/DashboardInsider'; import ItemsActionsBar from 'containers/Items/ItemsActionsBar'; @@ -26,6 +27,7 @@ import withItemsActions from 'containers/Items/withItemsActions'; import withViewsActions from 'containers/Views/withViewsActions'; + function ItemsList({ // #withDashboard changePageTitle, @@ -46,6 +48,8 @@ function ItemsList({ const [selectedRows, setSelectedRows] = useState([]); const [tableLoading, setTableLoading] = useState(false); + const { formatMessage } = useIntl(); + useEffect(() => { changePageTitle('Items List'); }, [changePageTitle]); @@ -75,7 +79,12 @@ function ItemsList({ // handle confirm delete item. const handleConfirmDeleteItem = useCallback(() => { requestDeleteItem(deleteItem.id).then(() => { - AppToaster.show({ message: 'the_item_has_been_deleted' }); + AppToaster.show({ + message: formatMessage({ + id: 'the_item_has_been_successfully_deleted', + }), + intent: Intent.SUCCESS, + }); setDeleteItem(false); }); }, [requestDeleteItem, deleteItem]); @@ -142,15 +151,15 @@ function ItemsList({

- Are you sure you want to move filename to Trash? You will be able to restore it later, - but it will become private to you. +

diff --git a/client/src/containers/Views/ViewForm.container.js b/client/src/containers/Views/ViewForm.container.js index dbd50d3aa..4a426db04 100644 --- a/client/src/containers/Views/ViewForm.container.js +++ b/client/src/containers/Views/ViewForm.container.js @@ -18,7 +18,7 @@ const viewFormConnect = connect(mapStateToProps); export default compose( withDashboard, withViewsActions, - withViewsDetails(), + withViewsDetails, viewFormConnect, withResourceDetail(), ); \ No newline at end of file diff --git a/client/src/containers/Views/ViewFormPage.js b/client/src/containers/Views/ViewFormPage.js index c27e5b4d0..50812b89d 100644 --- a/client/src/containers/Views/ViewFormPage.js +++ b/client/src/containers/Views/ViewFormPage.js @@ -2,6 +2,8 @@ import React, {useEffect, useState, useMemo, useCallback} from 'react'; import { useAsync } from 'react-use'; import { useParams } from 'react-router-dom'; import { Intent, Alert } from '@blueprintjs/core'; +import { FormattedHTMLMessage, useIntl } from 'react-intl'; + import DashboardInsider from 'components/Dashboard/DashboardInsider'; import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; import ViewForm from 'containers/Views/ViewForm'; @@ -30,6 +32,8 @@ function ViewFormPage({ const { resource_slug: resourceSlug, view_id: viewId } = useParams(); const [stateDeleteView, setStateDeleteView] = useState(null); + const { formatMessage } = useIntl(); + const fetchHook = useAsync(async () => { return Promise.all([ ...(resourceSlug) ? [ @@ -67,7 +71,9 @@ function ViewFormPage({ requestDeleteView(stateDeleteView.id).then((response) => { setStateDeleteView(null); AppToaster.show({ - message: 'the_custom_view_has_been_deleted', + message: formatMessage({ + id: 'the_custom_view_has_been_successfully_deleted', + }), intent: Intent.SUCCESS, }); }) @@ -87,15 +93,15 @@ function ViewFormPage({

- Are you sure you want to move filename to Trash? You will be able to restore it later, - but it will become private to you. +

diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js index e45a3733a..00c688cbe 100644 --- a/client/src/lang/en/index.js +++ b/client/src/lang/en/index.js @@ -27,4 +27,37 @@ export default { 'need_bigcapital_account?': 'Need a Bigcapital account ?', 'show': 'Show', 'hide': 'Hide', + 'item': 'Item', + 'account': 'Account', + 'service_has_been_successful_created': '{service} {name} has been successfully created.', + 'service_has_been_successful_edited': '{service} {name} has been successfully edited.', + 'you_are_about_permanently_delete_this_journal': `You're about to permanently delete this journal and all its transactions on accounts and attachments, and all of its data.

If you're not sure, you can archive this journal instead.`, + 'once_delete_these_accounts_you_will_not_able_restore_them': 'Once you delete these accounts, you won\'t be able to retrieve them later. Are you sure you want to delete them?', + 'once_delete_these_service_you_will_not_able_restore_it': 'Once you delete these {service}, you won\'t be able to retrieve them later. Are you sure you want to delete this {service}?', + 'you_could_not_delete_predefined_accounts': 'You could\'t delete predefined accounts.', + 'cannot_delete_account_has_associated_transactions': 'you could\'t not delete account that has associated transactions.', + 'the_account_has_been_successfully_inactivated': 'The account has been successfully inactivated.', + 'the_account_has_been_successfully_activated': 'The account has been successfully activated.', + 'the_account_has_been_successfully_deleted': 'The account has been successfully deleted.', + 'the_accounts_has_been_successfully_deleted': 'The accounts have been successfully deleted.', + 'are_sure_to_inactive_this_account': 'Are you sure you want to inactive this account? You will be able to activate it later', + 'are_sure_to_activate_this_account': 'Are you sure you want to activate this account? You will be able to inactivate it later', + + 'once_delete_this_account_you_will_able_to_restore_it': `Once you delete this account, you won\'t be able to restore it later. Are you sure you want to delete this account?

If you're not sure, you can inactivate this account instead.`, + 'the_journal_has_been_successfully_created': 'The journal #{number} has been successfully created.', + 'the_journal_has_been_successfully_edited': 'The journal #{number} has been successfully edited.', + + 'credit': 'Credit', + 'debit': 'Debit', + + 'once_delete_this_item_you_will_able_to_restore_it': `Once you delete this item, you won\'t be able to restore the item later. Are you sure you want to delete ?

If you're not sure, you can inactivate it instead.`, + 'the_item_has_been_successfully_deleted': 'The item has been successfully deleted.', + + 'the_item_category_has_been_successfully_created': 'The item category has been successfully created.', + 'the_item_category_has_been_successfully_edited': 'The item category has been successfully edited.', + + 'once_delete_these_views_you_will_not_able_restore_them': 'Once you delete the custom view, you won\'t be able to restore it later. Are you sure you want to delete this view?', + 'the_custom_view_has_been_successfully_deleted': 'The custom view has been successfully deleted.', + + 'teammate_invited_to_organization_account': 'Your teammate has been invited to the organization account.' }; \ No newline at end of file diff --git a/client/src/store/accounts/accounts.actions.js b/client/src/store/accounts/accounts.actions.js index 8ea722fa5..09ac0f07b 100644 --- a/client/src/store/accounts/accounts.actions.js +++ b/client/src/store/accounts/accounts.actions.js @@ -177,7 +177,7 @@ export const editAccount = ({ id, form }) => { }); }; -export const activeAccount = ({ id }) => { +export const activateAccount = ({ id }) => { return dispatch => ApiService.post(`accounts/${id}/active`); }; @@ -205,7 +205,11 @@ export const deleteBulkAccounts = ({ ids }) => { }); resolve(response); }).catch((error) => { - reject(error.response.data.errors || []); + const { response } = error; + const { data } = response; + const { errors } = data; + + reject(errors); }); }); }; diff --git a/client/src/style/pages/accounts-chart.scss b/client/src/style/pages/accounts-chart.scss index a02a69859..b3ed36c07 100644 --- a/client/src/style/pages/accounts-chart.scss +++ b/client/src/style/pages/accounts-chart.scss @@ -25,6 +25,15 @@ .code{ color: #666; } + .normal{ + .bp3-popover-wrapper{ + width: 100%; + } + .bp3-tooltip-indicator{ + cursor: initial; + border-bottom: 0; + } + } .actions{ padding-right: 18px; justify-content: right; diff --git a/client/src/style/pages/manual-journals.scss b/client/src/style/pages/manual-journals.scss index 5e130f152..847ff7e5e 100644 --- a/client/src/style/pages/manual-journals.scss +++ b/client/src/style/pages/manual-journals.scss @@ -16,6 +16,10 @@ .bp3-icon{ color: #666; } + .bp3-tooltip-indicator{ + cursor: initial; + border-bottom: 0; + } } .status{ font-size: 13px; diff --git a/server/src/http/controllers/Accounts.js b/server/src/http/controllers/Accounts.js index aa10f45c0..069850920 100644 --- a/server/src/http/controllers/Accounts.js +++ b/server/src/http/controllers/Accounts.js @@ -398,14 +398,16 @@ export default { async handler(req, res) { const { id } = req.params; const { Account } = req.models; - const account = await Account.findById(id); + const account = await Account.query().findById(id); if (!account) { return res.status(400).send({ errors: [{ type: 'ACCOUNT.NOT.FOUND', code: 100 }], }); } - await account.patch({ active: true }); + await Account.query() + .where('id', id) + .patch({ active: true }); return res.status(200).send({ id: account.id }); }, @@ -421,14 +423,16 @@ export default { async handler(req, res) { const { id } = req.params; const { Account } = req.models; - const account = await Account.findById(id); + const account = await Account.query().findById(id); if (!account) { return res.status(400).send({ errors: [{ type: 'ACCOUNT.NOT.FOUND', code: 100 }], }); } - await account.patch({ active: false }); + await Account.query() + .where('id', id) + .patch({ active: false }); return res.status(200).send({ id: account.id }); }, @@ -488,10 +492,23 @@ export default { }); const accountsIds = accounts.map((a) => a.id); const notFoundAccounts = difference(filter.ids, accountsIds); + const predefinedAccounts = accounts.filter(account => account.predefined); + const errorReasons = []; if (notFoundAccounts.length > 0) { return res.status(404).send({ - errors: [{ type: 'ACCOUNTS.IDS.NOT.FOUND', code: 200, ids: notFoundAccounts }], + errors: [{ + type: 'ACCOUNTS.IDS.NOT.FOUND', + code: 200, + ids: notFoundAccounts, + }], + }); + } + if (predefinedAccounts.length > 0) { + errorReasons.push({ + type: 'ACCOUNT.PREDEFINED', + code: 200, + ids: predefinedAccounts.map(a => a.id), }); } const accountsTransactions = await AccountTransaction.query() @@ -508,10 +525,15 @@ export default { } }); if (accountsHasTransactions.length > 0) { - return res.status(400).send({ - errors: [{ type: 'ACCOUNTS.HAS.TRANSACTIONS', code: 300, ids: accountsHasTransactions }], + errorReasons.push({ + type: 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS', + code: 300, + ids: accountsHasTransactions }); } + if (errorReasons.length > 0) { + return res.status(400).send({ errors: errorReasons }); + } await Account.query() .whereIn('id', accounts.map((a) => a.id)) .delete();