Skip to content

Commit

Permalink
[CMS] feat: add proposal spending summary and details (#2032)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagoalvesdulce committed Jul 28, 2020
1 parent cb568ce commit fa93638
Show file tree
Hide file tree
Showing 27 changed files with 457 additions and 44 deletions.
27 changes: 27 additions & 0 deletions src/actions/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,33 @@ export const onPayApprovedInvoices = () => (dispatch) => {
});
};

export const onGetSpendingSummary = () => (dispatch) => {
dispatch(act.REQUEST_SPENDING_SUMMARY({}));
return api
.getSpendingSummary()
.then((response) => {
dispatch(act.RECEIVE_SPENDING_SUMMARY(response));
})
.catch((error) => {
dispatch(act.RECEIVE_SPENDING_SUMMARY(null, error));
});
};

export const onFetchSpendingDetails = (token) =>
withCsrf((dispatch, _, csrf) => {
dispatch(act.REQUEST_SPENDING_DETAILS({ token }));
return api
.getSpendingDetails(csrf, token)
.then((response) => {
dispatch(act.RECEIVE_SPENDING_DETAILS({ ...response }));
return response;
})
.catch((error) => {
dispatch(act.RECEIVE_SPENDING_DETAILS(null, error));
throw error;
});
});

export const onFetchExchangeRate = (month, year) =>
withCsrf((dispatch, _, csrf) => {
dispatch(act.REQUEST_EXCHANGE_RATE({ month, year }));
Expand Down
6 changes: 6 additions & 0 deletions src/actions/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ export const RECEIVE_INVOICE_PAYOUTS = "API_RECEIVE_INVOICE_PAYOUTS";
export const REQUEST_PAY_APPROVED = "API_REQUEST_PAY_APPROVED";
export const RECEIVE_PAY_APPROVED = "API_RECEIVE_PAY_APPROVED";

export const REQUEST_SPENDING_SUMMARY = "API_REQUEST_SPENDING_SUMMARY";
export const RECEIVE_SPENDING_SUMMARY = "API_RECEIVE_SPENDING_SUMMARY";

export const REQUEST_SPENDING_DETAILS = "API_REQUEST_SPENDING_DETAILS";
export const RECEIVE_SPENDING_DETAILS = "API_RECEIVE_SPENDING_DETAILS";

export const REQUEST_EXCHANGE_RATE = "API_REQUEST_EXCHANGE_RATE";
export const RECEIVE_EXCHANGE_RATE = "API_RECEIVE_EXCHANGE_RATE";

Expand Down
10 changes: 7 additions & 3 deletions src/components/InvoiceDatasheet/InvoiceDatasheet.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const TableRow = ({ children, className }) => (

const InvoiceDatasheet = React.memo(function InvoiceDatasheet({
value,
omit,
onChange,
readOnly,
userRate,
Expand Down Expand Up @@ -105,7 +106,8 @@ const InvoiceDatasheet = React.memo(function InvoiceDatasheet({
currentRate,
policy,
proposalsOptions,
subContractors
subContractors,
omit
);
setGrid(grid);
},
Expand All @@ -116,7 +118,8 @@ const InvoiceDatasheet = React.memo(function InvoiceDatasheet({
currentRate,
policy,
proposalsOptions,
subContractors
subContractors,
omit
]
);

Expand Down Expand Up @@ -182,7 +185,7 @@ const InvoiceDatasheet = React.memo(function InvoiceDatasheet({
[onChange, value, grid.length]
);

const headers = useMemo(() => createTableHeaders(), []);
const headers = useMemo(() => createTableHeaders(omit), [omit]);

const onContextMenu = useCallback(
(e, cell) => (cell.readOnly ? e.preventDefault() : null),
Expand Down Expand Up @@ -251,6 +254,7 @@ const InvoiceDatasheet = React.memo(function InvoiceDatasheet({

InvoiceDatasheet.propTypes = {
value: PropTypes.array.isRequired,
omit: PropTypes.array,
readOnly: PropTypes.bool.isRequired,
onChange: PropTypes.func,
proposals: PropTypes.array.isRequired
Expand Down
75 changes: 42 additions & 33 deletions src/components/InvoiceDatasheet/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
proposalViewWrapper,
proposalSelectWrapper
} from "./wrappers";
import _omit from "lodash/fp/omit";

export const columnTypes = {
TYPE_COL: 1,
Expand Down Expand Up @@ -78,7 +79,8 @@ export const convertLineItemsToGrid = (
userRate = 0,
policy,
proposalsOptions,
subContractors
subContractors,
omit
) => {
const {
supporteddomains: policyDomains,
Expand All @@ -87,24 +89,25 @@ export const convertLineItemsToGrid = (
const grid = [];
const { grid: gridBody, expenseTotal, laborTotal, total } = lineItems.reduce(
(acc, line, idx) => {
const newLine = _omit(omit, line);
const isLaborReadonly =
line.type === 2 ? true : line.type === 3 ? true : readOnly;
const isSubContractorReadonly = line.type !== 4 ? true : readOnly;
newLine.type === 2 ? true : newLine.type === 3 ? true : readOnly;
const isSubContractorReadonly = newLine.type !== 4 ? true : readOnly;
const isExpenseReadonly =
line.type === 1 || line.type === 4 ? true : readOnly;
const laborHours = +fromMinutesToHours(line.labor);
const expenses = +fromUSDCentsToUSDUnits(line.expenses);
const subRate = +fromUSDCentsToUSDUnits(line.subrate);
newLine.type === 1 || newLine.type === 4 ? true : readOnly;
const laborHours = +fromMinutesToHours(newLine.labor);
const expenses = +fromUSDCentsToUSDUnits(newLine.expenses);
const subRate = +fromUSDCentsToUSDUnits(newLine.subrate);
const lineSubTotal =
line.type !== 4
newLine.type !== 4
? laborHours * userRate + expenses
: laborHours * subRate;
const rowErrors = (errors && errors[idx]) || {};
const newLine = [
const tableLine = [
{ readOnly: true, value: idx + 1 },
{
readOnly,
value: line.type,
value: newLine.type,
error: rowErrors && rowErrors.type,
dataEditor: selectWrapper(
policyLineItemTypes.map((op) => ({
Expand All @@ -115,7 +118,7 @@ export const convertLineItemsToGrid = (
},
{
readOnly,
value: line.domain,
value: newLine.domain,
error: rowErrors && rowErrors.domain,
dataEditor: selectWrapper(
policyDomains.map((op) => ({
Expand All @@ -126,35 +129,35 @@ export const convertLineItemsToGrid = (
},
{
readOnly,
value: line.subdomain,
value: newLine.subdomain,
error: rowErrors && rowErrors.subdomain
},
{
readOnly,
value: line.description,
value: newLine.description,
error: rowErrors && rowErrors.description,
dataEditor: textAreaWrapper({
error: rowErrors && rowErrors.description
}),
valueViewer: multilineTextWrapper()
},
{
newLine.proposaltoken && {
readOnly,
value: line.proposaltoken,
value: newLine.proposaltoken,
error: rowErrors && rowErrors.proposaltoken,
dataEditor: proposalSelectWrapper(proposalsOptions),
valueViewer: proposalViewWrapper(proposalsOptions)
},
{
readOnly: isSubContractorReadonly,
value: line.subuserid,
value: newLine.subuserid,
error: rowErrors && rowErrors.subuserid,
dataEditor: selectWrapper(getSubcontractorOptions(subContractors))
},
{
readOnly: isSubContractorReadonly,
error: rowErrors && rowErrors.subrate,
value: +fromUSDCentsToUSDUnits(line.subrate)
value: +fromUSDCentsToUSDUnits(newLine.subrate)
},
{
readOnly: isLaborReadonly,
Expand All @@ -172,9 +175,9 @@ export const convertLineItemsToGrid = (
}
];
return {
grid: acc.grid.concat([newLine]),
expenseTotal: acc.expenseTotal + line.expenses,
laborTotal: acc.laborTotal + line.labor,
grid: acc.grid.concat([tableLine.filter(Boolean)]),
expenseTotal: acc.expenseTotal + newLine.expenses,
laborTotal: acc.laborTotal + newLine.labor,
total: acc.total + lineSubTotal
};
},
Expand Down Expand Up @@ -231,19 +234,25 @@ export const convertGridToLineItems = (grid) => {
}, []);
};

export const createTableHeaders = () => [
{ readOnly: true, value: "", width: "2rem" },
{ value: "Type", readOnly: true, width: "4rem" },
{ value: "Domain", readOnly: true, width: "12rem" },
{ value: "Subdomain", readOnly: true, width: "14rem" },
{ value: "Description", readOnly: true, width: "30rem" },
{ value: "Proposal", readOnly: true, width: "30rem" },
{ value: "Subcontr. ID", readOnly: true, width: "10rem" },
{ value: "Subcontr. Rate (USD)", readOnly: true, width: "8rem" },
{ value: "Labor (hours)", readOnly: true, width: "7rem" },
{ value: "Expense (USD)", readOnly: true, width: "7.5rem" },
{ value: "Subtotal (USD)", readOnly: true, width: "7.5rem" }
];
export const createTableHeaders = (omit) =>
[
{ readOnly: true, value: "", width: "2rem" },
{ value: "Type", readOnly: true, width: "4rem" },
{ value: "Domain", readOnly: true, width: "12rem" },
{ value: "Subdomain", readOnly: true, width: "14rem" },
{ value: "Description", readOnly: true, width: "30rem" },
{
value: "Proposal Token",
key: "proposaltoken",
readOnly: true,
width: "10rem"
},
{ value: "Subcontr. ID", readOnly: true, width: "10rem" },
{ value: "Subcontr. Rate (USD)", readOnly: true, width: "8rem" },
{ value: "Labor (hours)", readOnly: true, width: "7rem" },
{ value: "Expense (USD)", readOnly: true, width: "7.5rem" },
{ value: "Subtotal (USD)", readOnly: true, width: "7.5rem" }
].filter((header) => (omit ? !omit.includes(header.key) : true));

export const updateGridCell = (grid, row, col, values) => {
grid[row][col] = { ...grid[row][col], ...values };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const PageDetails = ({
title,
subtitle,
actionsContent,
actionsClassName,
children,
headerClassName,
titleAndSubtitleWrapperClassName,
Expand Down Expand Up @@ -91,7 +92,10 @@ const PageDetails = ({
{titleContent}
{!!subtitle && <Subtitle>{subtitle}</Subtitle>}
</div>
<div className={styles.pageDetailsActions}>{actionsContent}</div>
<div
className={classNames(styles.pageDetailsActions, actionsClassName)}>
{actionsContent}
</div>
</div>

{children}
Expand Down
13 changes: 10 additions & 3 deletions src/containers/Invoice/Admin/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { Row } from "src/components/layout";
import useModalContext from "src/hooks/utils/useModalContext";

const ActionsContent = ({ openInviteModal }) => {
const mobile = useMediaQuery("(max-width: 560px)");
const mobile = useMediaQuery("(max-width: 768px)");

const inviteContractorLink = (
<UILink className="cursor-pointer" onClick={openInviteModal}>
Expand All @@ -38,23 +38,30 @@ const ActionsContent = ({ openInviteModal }) => {
Payout summaries
</Link>
);
const proposalBillingSummaryLink = (
<Link className="cursor-pointer" to="/admin/proposalsbilling">
Proposal billing
</Link>
);

return (
<div>
<>
{!mobile ? (
<Row justify="space-between" className={styles.actionsWrapper}>
{inviteContractorLink}
{generatePayoutsLink}
{payoutSummariesLink}
{proposalBillingSummaryLink}
</Row>
) : (
<Dropdown title="Actions" className={styles.actionsWrapper}>
<DropdownItem>{inviteContractorLink}</DropdownItem>
<DropdownItem>{generatePayoutsLink}</DropdownItem>
<DropdownItem>{payoutSummariesLink}</DropdownItem>
<DropdownItem>{proposalBillingSummaryLink}</DropdownItem>
</Dropdown>
)}
</div>
</>
);
};

Expand Down
4 changes: 2 additions & 2 deletions src/containers/Invoice/Admin/List.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
}

.actionsWrapper {
width: 39rem;
width: 50rem;
}

/* EXTRA SMALL SCREENS*/
@media screen and (max-width: 560px) {
@media screen and (max-width: 768px) {
.actionsWrapper {
width: 9rem;
justify-content: flex-end;
Expand Down
Loading

0 comments on commit fa93638

Please sign in to comment.