diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerActionBar.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerActionBar.js
new file mode 100644
index 000000000..d7b899899
--- /dev/null
+++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerActionBar.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import { useHistory } from 'react-router-dom';
+import Icon from 'components/Icon';
+import { Button, Classes, NavbarGroup, Intent } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import { safeCallback } from 'utils';
+
+import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
+import withAlertsActions from 'containers/Alert/withAlertActions';
+import withDrawerActions from 'containers/Drawer/withDrawerActions';
+
+import { compose } from 'utils';
+
+/**
+ * Expense drawer action bar.
+ */
+function ExpenseDrawerActionBar({
+ // #ownProps
+ expense,
+
+ // #withAlertsDialog
+ openAlert,
+
+ // #withDrawerActions
+ closeDrawer,
+}) {
+ const history = useHistory();
+
+ // Handle the expense edit action.
+ const onEditExpense = () => {
+ if (expense) {
+ history.push(`/expenses/${expense.id}/edit`);
+ closeDrawer('expense-drawer');
+ }
+ };
+
+ // Handle the expense delete action.
+ const onDeleteExpense = () => {
+ if (expense) {
+ openAlert('expense-delete', { expenseId: expense.id });
+ closeDrawer('expense-drawer');
+ }
+ };
+
+ return (
+
+
+ }
+ text={}
+ onClick={safeCallback(onEditExpense)}
+ />
+
+ }
+ text={}
+ // intent={Intent.DANGER}
+ onClick={safeCallback(onDeleteExpense)}
+ />
+
+
+ );
+}
+
+export default compose(
+ withAlertsActions,
+ withDrawerActions,
+)(ExpenseDrawerActionBar);
diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerContent.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerContent.js
new file mode 100644
index 000000000..37f3bd8cb
--- /dev/null
+++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerContent.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import { ExpenseDrawerProvider } from './ExpenseDrawerProvider';
+import ExpenseDrawerDetails from './ExpenseDrawerDetails';
+
+/**
+ * Expense drawer content.
+ */
+export default function ExpenseDrawerContent({
+ // #ownProp
+ expenseId,
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerDetails.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerDetails.js
new file mode 100644
index 000000000..763d6304d
--- /dev/null
+++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerDetails.js
@@ -0,0 +1,24 @@
+import React from 'react';
+import ExpenseDrawerActionBar from './ExpenseDrawerActionBar';
+import ExpenseDrawerHeader from './ExpenseDrawerHeader';
+import ExpenseDrawerTable from './ExpenseDrawerTable';
+import ExpenseDrawerFooter from './ExpenseDrawerFooter';
+import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
+import 'style/components/Drawer/ViewDetails.scss';
+
+/**
+ * Expense view details.
+ */
+export default function ExpenseDrawerDetails() {
+ const { expense } = useExpenseDrawerContext();
+ return (
+
+ );
+}
diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerFooter.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerFooter.js
new file mode 100644
index 000000000..d042363f4
--- /dev/null
+++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerFooter.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import { If, Money } from 'components';
+
+export default function ExpenseDrawerFooter({
+ expense: { total_amount, currency_code },
+}) {
+ return (
+
+ );
+}
diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerHeader.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerHeader.js
new file mode 100644
index 000000000..3161dfa56
--- /dev/null
+++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerHeader.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import moment from 'moment';
+import { If, Money } from 'components';
+import { FormattedMessage as T } from 'react-intl';
+
+/**
+ * Expense drawer content.
+ */
+export default function ExpenseDrawerHeader({
+ expense: {
+ total_amount,
+ payment_account: { name },
+ payment_date,
+ currency_code,
+ reference_no,
+ description,
+ published_at,
+ },
+}) {
+ return (
+
+
+
+
+
+
+
{moment(payment_date).format('YYYY MMM DD')}
+
+
+
+
+
+
+
{currency_code}
+
+
+
+
+
+
+
{moment(published_at).format('YYYY MMM DD')}
+
+
+
+ );
+}
diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerProvider.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerProvider.js
new file mode 100644
index 000000000..8ec487ad8
--- /dev/null
+++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerProvider.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import { useExpense } from 'hooks/query';
+import DashboardInsider from 'components/Dashboard/DashboardInsider';
+
+const ExpenseDrawerDrawerContext = React.createContext();
+
+/**
+ * Expense drawer provider.
+ */
+function ExpenseDrawerProvider({ expenseId, ...props }) {
+
+ // Fetch the expense details.
+ const { data: expense, isLoading: isExpenseLoading } = useExpense(expenseId, {
+ enabled: !!expenseId,
+ });
+
+ // provider.
+ const provider = {
+ expenseId,
+ expense,
+ };
+ return (
+
+
+
+ );
+}
+const useExpenseDrawerContext = () =>
+ React.useContext(ExpenseDrawerDrawerContext);
+
+export { ExpenseDrawerProvider, useExpenseDrawerContext };
diff --git a/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerTable.js b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerTable.js
new file mode 100644
index 000000000..f7c4e9652
--- /dev/null
+++ b/client/src/containers/Drawers/ExpenseDrawer/ExpenseDrawerTable.js
@@ -0,0 +1,40 @@
+import React from 'react';
+
+import { formatMessage } from 'services/intl';
+import { DataTable, Money } from 'components';
+
+/**
+ * Expense details table.
+ */
+export default function ExpenseDrawerTable({
+ expense: { currency_code, categories },
+}) {
+ const columns = React.useMemo(
+ () => [
+ {
+ Header: formatMessage({ id: 'Expense account' }),
+ accessor: 'expense_account.name',
+ width: 110,
+ },
+ {
+ Header: formatMessage({ id: 'Amount' }),
+ accessor: ({ amount }) => (
+
+ ),
+ width: 100,
+ },
+ {
+ Header: formatMessage({ id: 'description' }),
+ accessor: 'description',
+ width: 110,
+ },
+ ],
+ [],
+ );
+
+ return (
+
+
+
+ );
+}
diff --git a/client/src/containers/Drawers/ExpenseDrawer/index.js b/client/src/containers/Drawers/ExpenseDrawer/index.js
new file mode 100644
index 000000000..ae49b511a
--- /dev/null
+++ b/client/src/containers/Drawers/ExpenseDrawer/index.js
@@ -0,0 +1,36 @@
+import React, { lazy } from 'react';
+import { Drawer, DrawerSuspense } from 'components';
+import withDrawers from 'containers/Drawer/withDrawers';
+import withDrawerActions from 'containers/Drawer/withDrawerActions';
+
+import { compose } from 'utils';
+
+const ExpenseDrawerContent = lazy(() => import('./ExpenseDrawerContent'));
+
+/**
+ * Expense drawer.
+ */
+function ExpenseDrawer({
+ name,
+
+ //#withDrawer
+ isOpen,
+ payload: { expenseId, title },
+
+ closeDrawer,
+}) {
+ // Handle close drawer.
+ const handleDrawerClose = () => {
+ closeDrawer(name);
+ };
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default compose(withDrawers(), withDrawerActions)(ExpenseDrawer);
diff --git a/client/src/containers/Expenses/ExpensesLanding/ExpenseDataTable.js b/client/src/containers/Expenses/ExpensesLanding/ExpenseDataTable.js
index 365d91135..fcd95463d 100644
--- a/client/src/containers/Expenses/ExpensesLanding/ExpenseDataTable.js
+++ b/client/src/containers/Expenses/ExpensesLanding/ExpenseDataTable.js
@@ -12,6 +12,7 @@ import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withExpensesActions from './withExpensesActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
+import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { ActionsMenu, useExpensesTableColumns } from './components';
@@ -22,6 +23,9 @@ function ExpensesDataTable({
// #withExpensesActions
setExpensesTableState,
+ // #withDrawerActions
+ openDrawer,
+
// #withAlertsActions
openAlert,
}) {
@@ -32,7 +36,7 @@ function ExpensesDataTable({
isExpensesLoading,
isExpensesFetching,
- isEmptyStatus
+ isEmptyStatus,
} = useExpensesListContext();
const history = useHistory();
@@ -67,6 +71,14 @@ function ExpensesDataTable({
openAlert('expense-delete', { expenseId: expense.id });
};
+ // Handle view detail expense.
+ const handleViewDetailExpense = ({ id }) => {
+ openDrawer('expense-drawer', {
+ expenseId: id,
+ title: `Expense`,
+ });
+ };
+
// Display empty status instead of the table.
if (isEmptyStatus) {
return ;
@@ -76,34 +88,27 @@ function ExpensesDataTable({
);
@@ -112,5 +117,6 @@ function ExpensesDataTable({
export default compose(
withDashboardActions,
withAlertsActions,
+ withDrawerActions,
withExpensesActions,
)(ExpensesDataTable);
diff --git a/client/src/containers/Expenses/ExpensesLanding/components.js b/client/src/containers/Expenses/ExpensesLanding/components.js
index bb1693a43..5edb4644a 100644
--- a/client/src/containers/Expenses/ExpensesLanding/components.js
+++ b/client/src/containers/Expenses/ExpensesLanding/components.js
@@ -40,13 +40,14 @@ export function DescriptionAccessor(row) {
*/
export function ActionsMenu({
row: { original },
- payload: { onPublish, onEdit, onDelete },
+ payload: { onPublish, onEdit, onDelete, onViewDetails },
}) {
return (