feat: add purchases & sales tables.

This commit is contained in:
elforjani13
2022-06-30 22:05:35 +02:00
parent 6f2a456a56
commit 31fef21362
19 changed files with 496 additions and 38 deletions

View File

@@ -19,6 +19,8 @@ export const TABLES = {
WAREHOUSE_TRANSFERS: 'warehouse_transfers', WAREHOUSE_TRANSFERS: 'warehouse_transfers',
PROJECTS: 'projects', PROJECTS: 'projects',
TIMESHEETS: 'timesheets', TIMESHEETS: 'timesheets',
PURCHASES: 'purchases',
SALES: 'sales',
}; };
export const TABLE_SIZE = { export const TABLE_SIZE = {

View File

@@ -40,7 +40,13 @@ function ProjectDetailActionsBar({
// Handle new transaction button click. // Handle new transaction button click.
const handleNewTransactionBtnClick = ({ path }) => { const handleNewTransactionBtnClick = ({ path }) => {
history.push(`/${path}`); switch (path) {
case 'expense':
openDialog('expense-form', { projectId });
break;
case 'estimatedExpense':
openDialog('estimated-expense-form', { projectId });
}
}; };
const handleEditProjectBtnClick = () => { const handleEditProjectBtnClick = () => {
@@ -50,7 +56,9 @@ function ProjectDetailActionsBar({
}; };
// Handle table row size change. // Handle table row size change.
const handleTableRowSizeChange = (size) => { const handleTableRowSizeChange = (size) => {
addSetting('timesheets', 'tableSize', size); addSetting('timesheets', 'tableSize', size) &&
addSetting('sales', 'tableSize', size) &&
addSetting('purchases', 'tableSize', size);
}; };
const handleTimeEntryBtnClick = () => { const handleTimeEntryBtnClick = () => {
@@ -67,8 +75,8 @@ function ProjectDetailActionsBar({
<NavbarGroup> <NavbarGroup>
<TransactionSelect <TransactionSelect
transactions={[ transactions={[
{ name: 'Invoice', path: 'invoices/new' }, { name: 'New Expense', path: 'expense' },
{ name: 'Expenses', path: 'expenses/new' }, { name: 'New Estimated Expense', path: 'estimatedExpense' },
]} ]}
onItemSelect={handleNewTransactionBtnClick} onItemSelect={handleNewTransactionBtnClick}
/> />
@@ -105,8 +113,6 @@ function ProjectDetailActionsBar({
initialValue={timesheetsTableSize} initialValue={timesheetsTableSize}
onChange={handleTableRowSizeChange} onChange={handleTableRowSizeChange}
/> />
<NavbarDivider />
<Button icon={<Icon icon="more-vert" iconSize={16} />} minimal={true} />
</NavbarGroup> </NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}> <NavbarGroup align={Alignment.RIGHT}>
<Button <Button

View File

@@ -2,8 +2,9 @@ import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Tabs, Tab } from '@blueprintjs/core'; import { Tabs, Tab } from '@blueprintjs/core';
import TimeSheets from './Timesheets';
import ProjectTimesheet from './ProjectTimesheet'; import Purchases from './Purchases';
import Sales from './Sales';
/** /**
* Project detail tabs. * Project detail tabs.
@@ -16,19 +17,24 @@ export default function ProjectDetailTabs() {
animate={true} animate={true}
large={true} large={true}
renderActiveTabPanelOnly={true} renderActiveTabPanelOnly={true}
defaultSelectedTabId={'timesheet'} defaultSelectedTabId={'purchases'}
> >
<Tab id="overview" title={intl.get('project_details.label.overview')} /> <Tab id="overview" title={intl.get('project_details.label.overview')} />
<Tab <Tab
id="timesheet" id="timesheet"
title={intl.get('project_details.label.timesheet')} title={intl.get('project_details.label.timesheet')}
panel={<ProjectTimesheet />} panel={<TimeSheets />}
/> />
<Tab <Tab
id="purchases" id="purchases"
title={intl.get('project_details.label.purchases')} title={intl.get('project_details.label.purchases')}
panel={<Purchases />}
/>
<Tab
id="sales"
title={intl.get('project_details.label.sales')}
panel={<Sales />}
/> />
<Tab id="sales" title={intl.get('project_details.label.sales')} />
<Tab id="journals" title={intl.get('project_details.label.journals')} /> <Tab id="journals" title={intl.get('project_details.label.journals')} />
</Tabs> </Tabs>
</ProjectTabsContent> </ProjectTabsContent>
@@ -42,6 +48,10 @@ const ProjectTabsContent = styled.div`
background-color: #fff; background-color: #fff;
border-bottom: 1px solid #d2dce2; border-bottom: 1px solid #d2dce2;
> *:not(:last-child) {
margin-right: 0;
}
&.bp3-large > .bp3-tab { &.bp3-large > .bp3-tab {
font-size: 15px; font-size: 15px;
font-weight: 400; font-weight: 400;
@@ -59,9 +69,11 @@ const ProjectTabsContent = styled.div`
} }
} }
.bp3-tab-panel { .bp3-tab-panel {
margin-top: 20px; /* margin: 20px 32px; */
/* margin: 20px; */
/* margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
padding: 0 25px; padding: 0 25px; */
} }
} }
`; `;

View File

@@ -0,0 +1,71 @@
// @ts-nocheck
import React from 'react';
import styled from 'styled-components';
import { DataTable } from 'components';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
import { TABLES } from 'common/tables';
import { useMemorizedColumnsWidths } from 'hooks';
import { usePurchasesColumns, ActionMenu } from './components';
import withSettings from '../../../../Settings/withSettings';
import { compose } from 'utils';
const Purchases = [
{
id: 1,
date: '2022-06-08T22:00:00.000Z',
type: 'Invoice',
transaction_no: 'Inv-12345',
due_date: '2022-06-08T22:00:00.000Z',
balance: '$100.00',
status: 'Paid',
total: '$100.00',
},
];
/**
* Purchases DataTable.
* @returns
*/
function PurchasesTable({
// #withSettings
purchasesTableSize,
}) {
// Retrieve purchases table columns.
const columns = usePurchasesColumns();
// Handle delete purchase.
const handleDeletePurchase = () => {};
// Local storage memorizing columns widths.
const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths(TABLES.PURCHASES);
return (
<DataTable
columns={columns}
data={Purchases}
// loading={}
// headerLoading={}
// progressBarLoading={}
manualSortBy={true}
selectionColumn={true}
noInitialFetch={true}
sticky={true}
ContextMenu={ActionMenu}
TableLoadingRenderer={TableSkeletonRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing}
size={purchasesTableSize}
payload={{
onDelete: handleDeletePurchase,
}}
/>
);
}
export default compose(
withSettings(({ purchasesSettings }) => ({
purchasesTableSize: purchasesSettings?.tableSize,
})),
)(PurchasesTable);

View File

@@ -0,0 +1,92 @@
import React from 'react';
import styled from 'styled-components';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from 'common/classes';
import { FormatDateCell, Icon, FormattedMessage as T } from 'components';
import { Menu, MenuItem, Intent } from '@blueprintjs/core';
import { safeCallback } from 'utils';
/**
* Table actions cell.
*/
export function ActionMenu({ payload: { onDelete }, row: { original } }) {
return (
<Menu>
<MenuItem
text={intl.get('purchases.action.delete')}
intent={Intent.DANGER}
onClick={safeCallback(onDelete, original)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
);
}
export function usePurchasesColumns() {
return React.useMemo(
() => [
{
id: 'date',
Header: intl.get('purchases.column.date'),
accessor: 'date',
Cell: FormatDateCell,
width: 120,
className: 'date',
clickable: true,
textOverview: true,
},
{
id: 'type',
Header: intl.get('purchases.column.type'),
accessor: 'type',
width: 120,
className: 'type',
clickable: true,
textOverview: true,
},
{
id: 'transaction_no',
Header: intl.get('purchases.column.transaction_no'),
accessor: 'transaction_no',
width: 120,
},
{
id: 'due_date',
Header: intl.get('purchases.column.due_date'),
accessor: 'due_date',
Cell: FormatDateCell,
width: 120,
className: 'due_date',
clickable: true,
textOverview: true,
},
{
id: 'balance',
Header: intl.get('purchases.column.balance'),
accessor: 'balance',
width: 120,
clickable: true,
align: 'right',
className: clsx(CLASSES.FONT_BOLD),
},
{
id: 'total',
Header: intl.get('purchases.column.total'),
accessor: 'total',
align: 'right',
width: 120,
className: clsx(CLASSES.FONT_BOLD),
},
{
id: 'status',
Header: intl.get('purchases.column.status'),
accessor: 'status',
width: 120,
className: 'status',
clickable: true,
},
],
[],
);
}

View File

@@ -0,0 +1,35 @@
import React from 'react';
import styled from 'styled-components';
import PurchasesTable from './PurchasesTable';
import { DashboardContentTable } from 'components';
/**
*
* @returns
*/
export default function Purchases() {
return (
<DashboardContentTable>
<PurchasesTable />
</DashboardContentTable>
);
}
const PurchasesContentTable = styled.div`
margin: 22px 20px;
border: 1px solid #d2dce2;
background: #fff;
/* .bigcapital-datatable {
display: flex;
flex-direction: column;
flex: 1 0 0;
.pagination {
margin-top: auto;
}
&:not(.has-pagination) {
padding-bottom: 30px;
}
} */
`;

View File

@@ -0,0 +1,71 @@
//@ts-nocheck
import React from 'react';
import { DataTable } from 'components';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
import { TABLES } from 'common/tables';
import { useMemorizedColumnsWidths } from 'hooks';
import { useSalesColumns, ActionMenu } from './components';
import withSettings from '../../../../Settings/withSettings';
import { compose } from 'utils';
const Sales = [
{
id: 1,
date: '2022-06-08T22:00:00.000Z',
type: 'Invoice',
transaction_no: 'Inv-12345',
due_date: '2022-06-08T22:00:00.000Z',
balance: '$100.00',
status: 'Paid',
total: '$100.00',
},
];
/**
* Sales DataTable.
* @returns
*/
function SalesTable({
// #withSettings
salesTableSize,
}) {
// Retrieve sales table columns.
const columns = useSalesColumns();
// Handle delete sale.
const handleDeleteSale = () => {};
// Local storage memorizing columns widths.
const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths(TABLES.SALES);
return (
<DataTable
columns={columns}
data={Sales}
// loading={}
// headerLoading={}
// progressBarLoading={}
manualSortBy={true}
selectionColumn={true}
noInitialFetch={true}
sticky={true}
ContextMenu={ActionMenu}
TableLoadingRenderer={TableSkeletonRows}
TableHeaderSkeletonRenderer={TableSkeletonHeader}
initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing}
size={salesTableSize}
payload={{
onDelete: handleDeleteSale,
}}
/>
);
}
export default compose(
withSettings(({ salesSettings }) => ({
salesTableSize: salesSettings?.tableSize,
})),
)(SalesTable);

View File

@@ -0,0 +1,92 @@
import React from 'react';
import styled from 'styled-components';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from 'common/classes';
import { FormatDateCell, Icon, FormattedMessage as T } from 'components';
import { Menu, MenuItem, Intent } from '@blueprintjs/core';
import { safeCallback } from 'utils';
/**
* Table actions cell.
*/
export function ActionMenu({ payload: { onDelete }, row: { original } }) {
return (
<Menu>
<MenuItem
text={intl.get('sales.action.delete')}
intent={Intent.DANGER}
onClick={safeCallback(onDelete, original)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
);
}
export function useSalesColumns() {
return React.useMemo(
() => [
{
id: 'date',
Header: intl.get('sales.column.date'),
accessor: 'date',
Cell: FormatDateCell,
width: 120,
className: 'date',
clickable: true,
textOverview: true,
},
{
id: 'type',
Header: intl.get('sales.column.type'),
accessor: 'type',
width: 120,
className: 'type',
clickable: true,
textOverview: true,
},
{
id: 'transaction_no',
Header: intl.get('sales.column.transaction_no'),
accessor: 'transaction_no',
width: 120,
},
{
id: 'due_date',
Header: intl.get('sales.column.due_date'),
accessor: 'due_date',
Cell: FormatDateCell,
width: 120,
className: 'due_date',
clickable: true,
textOverview: true,
},
{
id: 'balance',
Header: intl.get('sales.column.balance'),
accessor: 'balance',
width: 120,
clickable: true,
align: 'right',
className: clsx(CLASSES.FONT_BOLD),
},
{
id: 'total',
Header: intl.get('sales.column.total'),
accessor: 'total',
align: 'right',
width: 120,
className: clsx(CLASSES.FONT_BOLD),
},
{
id: 'status',
Header: intl.get('sales.column.status'),
accessor: 'status',
width: 120,
className: 'status',
clickable: true,
},
],
[],
);
}

View File

@@ -0,0 +1,18 @@
import React from 'react';
import styled from 'styled-components';
import SalesTable from './SalesTable';
import { DashboardContentTable } from 'components';
/**
*
* @returns
*/
export default function Sales() {
return (
<SalesContentTable>
<SalesTable />
</SalesContentTable>
);
}
const SalesContentTable = styled(DashboardContentTable)``;

View File

@@ -87,7 +87,7 @@ const TimesheetDataTable = styled(DataTable)`
.tbody { .tbody {
.tr .td { .tr .td {
padding: 0.5rem 0.8rem; /* padding: 0.5rem 0.8rem; */
} }
.avatar.td { .avatar.td {
@@ -114,11 +114,10 @@ const TimesheetDataTable = styled(DataTable)`
} }
} }
} }
}
.table-size--small { .table-size--small {
.tbody .tr { .tbody .tr {
height: 45px; height: 45px;
}
} }
} }
`; `;

View File

@@ -4,12 +4,11 @@ import styled from 'styled-components';
import { FormatDate, Icon, FormattedMessage as T } from 'components'; import { FormatDate, Icon, FormattedMessage as T } from 'components';
import { Menu, MenuItem, Intent } from '@blueprintjs/core'; import { Menu, MenuItem, Intent } from '@blueprintjs/core';
import { safeCallback, firstLettersArgs } from 'utils'; import { safeCallback, firstLettersArgs } from 'utils';
import { chain } from 'lodash';
/** /**
* Table actions cell. * Table actions cell.
*/ */
export function ActionsMenu({ export function ActionsMenu({
payload: { onDelete, onViewDetails }, payload: { onDelete, onViewDetails },
row: { original }, row: { original },

View File

@@ -8,19 +8,19 @@ import TimesheetsHeader from './TimesheetsHeader';
* Project Timesheet. * Project Timesheet.
* @returns * @returns
*/ */
export default function ProjectTimesheet() { export default function TimeSheets() {
return ( return (
<React.Fragment> <React.Fragment>
<TimesheetsHeader /> <TimesheetsHeader />
<ProjectTimesheetTableCard> <TimesheetTableCard>
<TimesheetsTable /> <TimesheetsTable />
</ProjectTimesheetTableCard> </TimesheetTableCard>
</React.Fragment> </React.Fragment>
); );
} }
const ProjectTimesheetTableCard = styled.div` const TimesheetTableCard = styled.div`
margin: 20px; margin: 22px 32px;
border: 1px solid #c8cad0; // #000a1e33 #f0f0f0 border: 1px solid #c8cad0; // #000a1e33 #f0f0f0
border-radius: 3px; border-radius: 3px;
background: #fff; background: #fff;

View File

@@ -29,17 +29,17 @@ export const FinancialProgressBar = ({ ...rest }) => {
const FinancialSectionWrap = styled.div` const FinancialSectionWrap = styled.div`
display: flex; display: flex;
flex-wrap: wrap; /* flex-wrap: wrap; */
margin: 20px 20px 20px; margin: 22px 32px;
gap: 10px; gap: 10px;
`; `;
const FinancialSectionCard = styled.div` const FinancialSectionCard = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-shrink: 0; flex-shrink: 1;
border-radius: 3px; border-radius: 3px;
width: 220px; width: 230px;
height: 116px; height: 116px;
background-color: #fff; background-color: #fff;
border: 1px solid #c8cad0; // #000a1e33 #f0f0f0 border: 1px solid #c8cad0; // #000a1e33 #f0f0f0

View File

@@ -39,6 +39,7 @@ const ProjectFormDialogRoot = styled(Dialog)`
.bp3-dialog-body { .bp3-dialog-body {
.bp3-form-group { .bp3-form-group {
margin-bottom: 15px; margin-bottom: 15px;
margin-top: 15px;
label.bp3-label { label.bp3-label {
margin-bottom: 3px; margin-bottom: 3px;

View File

@@ -1,3 +1,4 @@
//@ts-nocheck
import React from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
@@ -9,9 +10,8 @@ import {
Row, Row,
FormattedMessage as T, FormattedMessage as T,
} from 'components'; } from 'components';
import { modalChargeOptions } from '../../../../common/modalChargeOptions'; import { taskChargeOptions } from 'common/modalChargeOptions';
import { ChargeSelect } from '../../components';
import { TaskModalChargeSelect } from './components';
/** /**
* Task form fields. * Task form fields.
@@ -45,13 +45,16 @@ function TaskFormFields() {
label={<T id={'task.dialog.charge'} />} label={<T id={'task.dialog.charge'} />}
> >
<ControlGroup> <ControlGroup>
<TaskModalChargeSelect <ChargeSelect
name="taskCharge" name="taskCharge"
items={modalChargeOptions} items={taskChargeOptions}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
filterable={false} filterable={false}
/> />
<FInputGroup name="taskamount" /> <FInputGroup
name="taskamount"
disabled={values?.taskCharge === 'Non-chargeable'}
/>
</ControlGroup> </ControlGroup>
</FFormGroup> </FFormGroup>
</Col> </Col>

View File

@@ -23,7 +23,9 @@ export default (mapState) => {
vendorsCreditNoteSetting: state.settings.data.vendorCredit, vendorsCreditNoteSetting: state.settings.data.vendorCredit,
warehouseTransferSettings: state.settings.data.warehouseTransfers, warehouseTransferSettings: state.settings.data.warehouseTransfers,
projectSettings:state.settings.data.projects, projectSettings:state.settings.data.projects,
timesheetsSettings:state.settings.data.timesheets timesheetsSettings:state.settings.data.timesheets,
purchasesSettings:state.settings.data.purchases,
salesSettings:state.settings.data.sales,
}; };
return mapState ? mapState(mapped, state, props) : mapped; return mapState ? mapState(mapped, state, props) : mapped;
}; };

View File

@@ -2062,6 +2062,9 @@
"task.dialog.estimated_hours": "Estimate Hours", "task.dialog.estimated_hours": "Estimate Hours",
"task.dialog.charge": "Charge", "task.dialog.charge": "Charge",
"task.dialog.estimated_amount": "Estimated Amount", "task.dialog.estimated_amount": "Estimated Amount",
"task.dialog.hourly_rate": "Hourly rate",
"task.dialog.fixed_price": "Fixed price",
"task.dialog.non_chargeable": "Non-chargeable",
"project.schema.label.contact": "Contact", "project.schema.label.contact": "Contact",
"project.schema.label.project_name": "Project name", "project.schema.label.project_name": "Project name",
"project.schema.label.deadline": "Deadline", "project.schema.label.deadline": "Deadline",
@@ -2097,5 +2100,51 @@
"time_entry.schema.label.duration": "Duration", "time_entry.schema.label.duration": "Duration",
"time_entry.schema.label.date": "Date", "time_entry.schema.label.date": "Date",
"find_or_choose_a_project": "Find or choose a project", "find_or_choose_a_project": "Find or choose a project",
"choose_a_task": "Choose a task" "choose_a_task": "Choose a task",
"expenses.dialog.label": "New Expense",
"expenses.dialog.expense_name": "Expense Name",
"expenses.dialog.expense_date": "Date",
"expenses.dialog.quantity": "Quantity",
"expenses.dialog.charge": "Charge",
"expenses.dialog.track_expense": "Track to Expense",
"expenses.dialog.unit_price": "Unit Price",
"expenses.dialog.expense_total": "Total",
"expenses.dialog.percentage": "Percentage",
"expenses.dialog.total":"Total:",
"expenses.dialog.markup": "% Markup",
"expenses.dialog.pass_cost_on": "Pass cost on",
"expenses.dialog.custom_pirce": "Custom Pirce",
"expenses.dialog.non_chargeable": "Non-chargeable",
"expense.schema.label.expense_name": "Expense name",
"expense.schema.label.estimated_expense": "Estimated expense",
"expense.schema.label.quantity": "Quantity",
"expense.schema.label.unit_price": "Unit price",
"choose_an_estimated_expense": "Choose an estimated expense",
"estimated_expenses.dialog.label": "New Estimated Expense",
"estimated_expenses.dialog.estimated_expense": "Estimated Expense Name",
"estimated_expenses.dialog.quantity": "Quantity",
"estimated_expenses.dialog.unit_price": "Unit Price",
"estimated_expenses.dialog.total": "Total",
"estimated_expenses.dialog.charge": "Charge",
"estimated_expenses.dialog.percentage": "Percentage",
"estimated_expenses.dialog.estimated_amount": "Estimated Amount:",
"estimated_expense.schema.label.estimated_expense": "Estimated expense name",
"estimated_expense.schema.label.quantity": "Quantity",
"estimated_expense.schema.label.unit_price": "Unit price",
"purchases.column.date": "Date",
"purchases.column.type": "Type",
"purchases.column.transaction_no": "Transaction No",
"purchases.column.due_date": "Due Date",
"purchases.column.balance": "Balance",
"purchases.column.total": "Total",
"purchases.column.status": "Status",
"purchases.action.delete": "Delete",
"sales.column.date": "Date",
"sales.column.type": "Type",
"sales.column.transaction_no": "Transaction No",
"sales.column.due_date": "Due Date",
"sales.column.balance": "Balance",
"sales.column.total": "Total",
"sales.column.status": "Status",
"sales.action.delete": "Delete"
} }

View File

@@ -67,6 +67,12 @@ const initialState = {
timesheets: { timesheets: {
tableSize: 'medium', tableSize: 'medium',
}, },
purchases: {
tableSize: 'medium',
},
sales: {
tableSize: 'medium',
}
}, },
}; };