diff --git a/client/src/containers/Alerts/Contacts/ContactActivateAlert.js b/client/src/containers/Alerts/Contacts/ContactActivateAlert.js new file mode 100644 index 000000000..965ed7384 --- /dev/null +++ b/client/src/containers/Alerts/Contacts/ContactActivateAlert.js @@ -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 ( + } + confirmButtonText={} + intent={Intent.WARNING} + isOpen={isOpen} + onCancel={handleCancelActivateContact} + loading={isLoading} + onConfirm={handleConfirmContactActivate} + > +

{intl.get('are_sure_to_activate_this_contact')}

+
+ ); +} + +export default compose( + withAlertStoreConnect(), + withAlertActions, +)(ContactActivateAlert); diff --git a/client/src/containers/Alerts/Contacts/ContactInactivateAlert.js b/client/src/containers/Alerts/Contacts/ContactInactivateAlert.js new file mode 100644 index 000000000..d0961c424 --- /dev/null +++ b/client/src/containers/Alerts/Contacts/ContactInactivateAlert.js @@ -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 ( + } + confirmButtonText={} + intent={Intent.WARNING} + isOpen={isOpen} + onCancel={handleCancelInactivateContact} + onConfirm={handleConfirmContactInactive} + loading={isLoading} + > +

+ {intl.get('are_sure_to_inactive_this_contact', { + name: service, + })} +

+
+ ); +} + +export default compose( + withAlertStoreConnect(), + withAlertActions, +)(ContactInactivateAlert); diff --git a/client/src/containers/Customers/CustomersAlerts.js b/client/src/containers/Customers/CustomersAlerts.js index d56acb26c..6a51cd7a0 100644 --- a/client/src/containers/Customers/CustomersAlerts.js +++ b/client/src/containers/Customers/CustomersAlerts.js @@ -1,6 +1,8 @@ import React from 'react'; import CustomerDeleteAlert from 'containers/Alerts/Customers/CustomerDeleteAlert'; // import CustomerBulkDeleteAlert from 'containers/Alerts/Customers/CustomerBulkDeleteAlert'; +import ContactActivateAlert from '../../containers/Alerts/Contacts/ContactActivateAlert'; +import ContactInactivateAlert from '../../containers/Alerts/Contacts/ContactInactivateAlert'; /** * Customers alert. @@ -9,6 +11,8 @@ export default function ItemsAlerts() { return (
+ + {/* */}
); diff --git a/client/src/containers/Customers/CustomersLanding/CustomersTable.js b/client/src/containers/Customers/CustomersLanding/CustomersTable.js index 817718599..cef76a13a 100644 --- a/client/src/containers/Customers/CustomersLanding/CustomersTable.js +++ b/client/src/containers/Customers/CustomersLanding/CustomersTable.js @@ -72,6 +72,19 @@ function CustomersTable({ openDialog('contact-duplicate', { contactId: id }); }; + // Handle cancel/confirm inactive. + const handleInactiveCustomer = ({ id, contact_service }) => { + openAlert('contact-inactivate', { + contactId: id, + service: contact_service, + }); + }; + + // Handle cancel/confirm activate. + const handleActivateCustomer = ({ id, contact_service }) => { + openAlert('contact-activate', { contactId: id, service: contact_service }); + }; + if (isEmptyStatus) { return ; } @@ -102,6 +115,8 @@ function CustomersTable({ onDelete: handleCustomerDelete, onEdit: handleCustomerEdit, onDuplicate: handleContactDuplicate, + onInactivate: handleInactiveCustomer, + onActivate: handleActivateCustomer, }} ContextMenu={ActionsMenu} /> diff --git a/client/src/containers/Customers/CustomersLanding/components.js b/client/src/containers/Customers/CustomersLanding/components.js index 8db27dc6c..fa6f05064 100644 --- a/client/src/containers/Customers/CustomersLanding/components.js +++ b/client/src/containers/Customers/CustomersLanding/components.js @@ -8,7 +8,7 @@ import { Position, Intent, } from '@blueprintjs/core'; -import { Icon, Money } from 'components'; +import { Icon, Money, If } from 'components'; import { safeCallback } from 'utils'; import { firstLettersArgs } from 'utils'; import intl from 'react-intl-universal'; @@ -18,10 +18,8 @@ import intl from 'react-intl-universal'; */ export function ActionsMenu({ row: { original }, - payload: { onEdit, onDelete, onDuplicate }, + payload: { onEdit, onDelete, onDuplicate, onInactivate, onActivate }, }) { - - return ( + + } + onClick={safeCallback(onInactivate, original)} + /> + + + } + onClick={safeCallback(onActivate, original)} + /> + } text={intl.get('delete_customer')} @@ -74,8 +86,6 @@ export function BalanceAccessor(row) { * Retrieve customers table columns. */ export function useCustomersTableColumns() { - - return useMemo( () => [ { diff --git a/client/src/containers/Vendors/VendorsAlerts.js b/client/src/containers/Vendors/VendorsAlerts.js index d7cdec967..91cf41e2b 100644 --- a/client/src/containers/Vendors/VendorsAlerts.js +++ b/client/src/containers/Vendors/VendorsAlerts.js @@ -1,10 +1,14 @@ import React from 'react'; import VendorDeleteAlert from 'containers/Alerts/Vendors/VendorDeleteAlert'; +import ContactActivateAlert from '../../containers/Alerts/Contacts/ContactActivateAlert'; +import ContactInactivateAlert from '../../containers/Alerts/Contacts/ContactInactivateAlert'; export default function VendorsAlerts() { return (
+ +
); } diff --git a/client/src/containers/Vendors/VendorsLanding/VendorsTable.js b/client/src/containers/Vendors/VendorsLanding/VendorsTable.js index 18efd3718..d11299213 100644 --- a/client/src/containers/Vendors/VendorsLanding/VendorsTable.js +++ b/client/src/containers/Vendors/VendorsLanding/VendorsTable.js @@ -51,6 +51,19 @@ function VendorsTable({ history.push(`/vendors/${vendor.id}/edit`); }; + // Handle cancel/confirm inactive. + const handleInactiveVendor = ({ id, contact_service }) => { + openAlert('contact-inactivate', { + contactId: id, + service: contact_service, + }); + }; + + // Handle cancel/confirm activate. + const handleActivateVendor = ({ id, contact_service }) => { + openAlert('contact-activate', { contactId: id, service: contact_service }); + }; + // Handle click delete vendor. const handleDeleteVendor = ({ id }) => { openAlert('vendor-delete', { vendorId: id }); @@ -104,6 +117,8 @@ function VendorsTable({ onEdit: handleEditVendor, onDelete: handleDeleteVendor, onDuplicate: handleContactDuplicate, + onInactivate: handleInactiveVendor, + onActivate: handleActivateVendor, }} /> ); diff --git a/client/src/containers/Vendors/VendorsLanding/components.js b/client/src/containers/Vendors/VendorsLanding/components.js index 75b64c620..db312fd29 100644 --- a/client/src/containers/Vendors/VendorsLanding/components.js +++ b/client/src/containers/Vendors/VendorsLanding/components.js @@ -9,7 +9,7 @@ import { Intent, } from '@blueprintjs/core'; import intl from 'react-intl-universal'; -import { Icon, Money } from 'components'; +import { Icon, Money, If } from 'components'; import { safeCallback, firstLettersArgs } from 'utils'; /** @@ -17,10 +17,8 @@ import { safeCallback, firstLettersArgs } from 'utils'; */ export function ActionsMenu({ row: { original }, - payload: { onEdit, onDelete, onDuplicate }, + payload: { onEdit, onDelete, onDuplicate, onInactivate, onActivate }, }) { - - return ( + + } + onClick={safeCallback(onInactivate, original)} + /> + + + } + onClick={safeCallback(onActivate, original)} + /> + } text={intl.get('delete_vendor')} @@ -87,8 +99,6 @@ export function BalanceAccessor({ closing_balance, currency_code }) { * Retrieve the vendors table columns. */ export function useVendorsTableColumns() { - - return React.useMemo( () => [ { @@ -127,7 +137,7 @@ export function useVendorsTableColumns() { accessor: BalanceAccessor, className: 'receivable_balance', width: 100, - } + }, ], [], ); diff --git a/client/src/hooks/query/contacts.js b/client/src/hooks/query/contacts.js index b56df5071..2634c4060 100644 --- a/client/src/hooks/query/contacts.js +++ b/client/src/hooks/query/contacts.js @@ -1,5 +1,15 @@ +import { useMutation, useQueryClient } from 'react-query'; import useApiRequest from '../useRequest'; import { useQueryTenant } from '../useQueryRequest'; +import t from './types'; + +// Common invalidate queries. +const commonInvalidateQueries = (queryClient) => { + // Invalidate vendors. + queryClient.invalidateQueries(t.VENDORS); + // Invalidate customers. + queryClient.invalidateQueries(t.CUSTOMERS); +}; /** * Retrieve the contact duplicate. @@ -33,3 +43,41 @@ export function useAutoCompleteContacts(props) { }, ); } + +/** + * Activate the given Contact. + */ +export function useActivateContact(props) { + const queryClient = useQueryClient(); + const apiRequest = useApiRequest(); + + return useMutation((id) => apiRequest.post(`contacts/${id}/activate`), { + onSuccess: (res, id) => { + // Invalidate specific contact. + queryClient.invalidateQueries([t.CONTACT, id]); + + // Common invalidate queries. + commonInvalidateQueries(queryClient); + }, + ...props, + }); +} + +/** + * Inactivate the given contact. + */ +export function useInactivateContact(props) { + const queryClient = useQueryClient(); + const apiRequest = useApiRequest(); + + return useMutation((id) => apiRequest.post(`contacts/${id}/inactivate`), { + onSuccess: (res, id) => { + // Invalidate specific item. + queryClient.invalidateQueries([t.CONTACT, id]); + + // Common invalidate queries. + commonInvalidateQueries(queryClient); + }, + ...props, + }); +} diff --git a/client/src/hooks/query/types.js b/client/src/hooks/query/types.js index 61969118a..2b141bd1d 100644 --- a/client/src/hooks/query/types.js +++ b/client/src/hooks/query/types.js @@ -126,6 +126,11 @@ const LANDED_COSTS = { LANDED_COST_TRANSACTION: 'LANDED_COST_TRANSACTION', }; +const CONTACTS = { + CONTACTS: 'CONTACTS', + CONTACT: 'CONTACT', +}; + export default { ...ACCOUNTS, ...BILLS, @@ -147,4 +152,5 @@ export default { ...EXPENSES, ...MANUAL_JOURNALS, ...LANDED_COSTS, + ...CONTACTS, }; diff --git a/client/src/lang/ar-ly/index.json b/client/src/lang/ar-ly/index.json index 6a6e84ae4..e031387fc 100644 --- a/client/src/lang/ar-ly/index.json +++ b/client/src/lang/ar-ly/index.json @@ -1198,5 +1198,12 @@ "non-inventory":"غير مخزون", "terms_conditions":"الشروط والأحكام", "your_invoice_numbers_are_set_on_auto_increment_mod_are_you_sure_changing_this_setting":"Your invoice numbers are set on auto-increment mod. Are you sure changing this setting?", - "auto_incrementing_number":"Auto-incrementing number" -} \ No newline at end of file + "auto_incrementing_number":"Auto-incrementing number", + "the_inventory_adjustment_has_been_published": "تم نشر تسوية المخزون", + "are_sure_to_publish_this_inventory_adjustment": "هل أنت متأكد أنك تريد نشر هذا المخزون ؟", + "the_contact_has_been_activated_successfully": "تم تفعيل جهة اتصال بنجاح.", + "the_contact_has_been_inactivated_successfully": "تم إلغاء تنشيط جهة اتصال بنجاح.", + "are_sure_to_inactive_this_contact": "هل أنت متأكد أنك تريد إلغاء تنشيط جهة اتصال؟ ستكون قادرًا على تنشيطه لاحقًا", + "are_sure_to_activate_this_contact": "هل أنت متأكد أنك تريد تفعيل جهة اتصال؟ ستتمكن من تعطيله لاحقًا" +} + diff --git a/client/src/lang/en/index.json b/client/src/lang/en/index.json index 271d77efa..48989a679 100644 --- a/client/src/lang/en/index.json +++ b/client/src/lang/en/index.json @@ -1187,6 +1187,9 @@ "your_invoice_numbers_are_set_on_auto_increment_mod_are_you_sure_changing_this_setting":"Your invoice numbers are set on auto-increment mod. Are you sure changing this setting?", "auto_incrementing_number":"Auto-incrementing number", "the_inventory_adjustment_has_been_published": "The Inventory adjustment has been published", - "are_sure_to_publish_this_inventory_adjustment": "Are you sure you want to publish this inventory?" - + "are_sure_to_publish_this_inventory_adjustment": "Are you sure you want to publish this inventory?", + "the_contact_has_been_activated_successfully": "The contact has been inactivated successfully.", + "the_contact_has_been_inactivated_successfully": "The contact has been inactivated successfully.", + "are_sure_to_inactive_this_contact": "Are you sure you want to inactive this contact ? You will be able to activate it later", + "are_sure_to_activate_this_contact": "Are you sure you want to activate this contact ? You will be able to inactivate it later" } \ No newline at end of file