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',
PROJECTS: 'projects',
TIMESHEETS: 'timesheets',
PURCHASES: 'purchases',
SALES: 'sales',
};
export const TABLE_SIZE = {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,9 @@ export default (mapState) => {
vendorsCreditNoteSetting: state.settings.data.vendorCredit,
warehouseTransferSettings: state.settings.data.warehouseTransfers,
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;
};

View File

@@ -2062,6 +2062,9 @@
"task.dialog.estimated_hours": "Estimate Hours",
"task.dialog.charge": "Charge",
"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.project_name": "Project name",
"project.schema.label.deadline": "Deadline",
@@ -2097,5 +2100,51 @@
"time_entry.schema.label.duration": "Duration",
"time_entry.schema.label.date": "Date",
"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: {
tableSize: 'medium',
},
purchases: {
tableSize: 'medium',
},
sales: {
tableSize: 'medium',
}
},
};