Skip to content
This repository has been archived by the owner on Nov 6, 2023. It is now read-only.

Commit

Permalink
perf(cards): add sales detail to boards and calendar (#4679)
Browse files Browse the repository at this point in the history
* perf(cards): add sales detail to boards and calendar

* little fix

* little fix

* little fix

---------

Co-authored-by: Anu-Ujin Bat-Ulzii <[email protected]>
  • Loading branch information
ariunzayarin and Anu-Ujin authored Oct 11, 2023
1 parent 835a043 commit 3c76fdd
Show file tree
Hide file tree
Showing 8 changed files with 349 additions and 36 deletions.
135 changes: 125 additions & 10 deletions packages/plugin-cards-ui/src/deals/components/CalendarColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import styled from 'styled-components';
import options from '@erxes/ui-cards/src/deals/options';
import { IDeal, IDealTotalAmount } from '@erxes/ui-cards/src/deals/types';
import Deal from '@erxes/ui-cards/src/deals/components/DealItem';
import styledTS from 'styled-components-ts';

type Props = {
deals: IDeal[];
Expand All @@ -22,16 +23,31 @@ type Props = {
onLoadMore: (skip: number) => void;
};

const Amount = styled.ul`
const Amount = styledTS<{ showAll: boolean }>(styled.ul)`
list-style: none;
overflow: hidden;
margin: 0 0 5px;
padding: 0 16px;
${props =>
props.showAll === false
? `
height: 20px;
overflow: hidden;
transition: all 300ms ease-out;
`
: `
height: unset;
`}
li {
padding-right: 5px;
font-size: 12px;
> div {
float: right;
}
span {
font-weight: bold;
font-size: 10px;
Expand All @@ -45,9 +61,25 @@ const Amount = styled.ul`
content: '';
}
}
div {
display: inline;
}
`;

class DealColumn extends React.Component<Props, {}> {
componentDidMount() {
window.addEventListener('storageChange', this.handleStorageChange);
}

componentWillUnmount() {
window.removeEventListener('storageChange', this.handleStorageChange);
}

handleStorageChange = () => {
this.forceUpdate();
};

onLoadMore = () => {
const { deals, onLoadMore } = this.props;
onLoadMore(deals.length);
Expand All @@ -69,7 +101,7 @@ class DealColumn extends React.Component<Props, {}> {

renderAmount(currencies: [{ name: string; amount: number }]) {
return currencies.map((total, index) => (
<div key={index} style={{ display: 'inline' }}>
<div key={index}>
{total.amount.toLocaleString()}{' '}
<span>
{total.name}
Expand All @@ -80,21 +112,104 @@ class DealColumn extends React.Component<Props, {}> {
}

renderTotalAmount() {
const { dealTotalAmounts } = this.props;
const { dealTotalAmounts, deals } = this.props;
const totalForType = dealTotalAmounts || [];

return (
<Amount>
{totalForType.map(type => (
<li key={type._id}>
<span>{type.name}: </span>
{this.renderAmount(type.currencies)}
const forecastArray = [];
const totalAmountArray = [];

dealTotalAmounts.map(total =>
total.currencies.map(currency => totalAmountArray.push(currency))
);

this.props.deals.map(deal => {
const probability =
deal.stage.probability === 'Won'
? '100%'
: deal.stage.probability === 'Lost'
? '0%'
: deal.stage.probability;

Object.keys(deal.amount).map(key =>
forecastArray.push({
name: key,
amount: deal.amount[key] as number,
probability: parseInt(probability, 10)
})
);
});

const detail = () => {
if (!deals || deals.length === 0) {
return null;
}

return (
<>
<li>
<span>Total ({deals.length}): </span>
{this.renderPercentedAmount(totalAmountArray)}
</li>
))}
<li>
<span>Forecasted: </span>
{this.renderPercentedAmount(forecastArray)}
</li>
</>
);
};

return (
<Amount
showAll={
localStorage.getItem('showSalesDetail') === 'true' ? true : false
}
>
{detail()}
{totalForType.map(type => {
if (type.name === 'In progress') {
return null;
}

const percent = type.name === 'Won' ? '100%' : '0%';

return (
<li key={type._id}>
<span>
{type.name} ({percent}):{' '}
</span>
{this.renderAmount(type.currencies)}
</li>
);
})}
</Amount>
);
}

renderPercentedAmount(currencies) {
const sumByName = {};

currencies.forEach(item => {
const { name, amount, probability = 100 } = item;
if (sumByName[name] === undefined) {
sumByName[name] = (amount * probability) / 100;
} else {
sumByName[name] += (amount * probability) / 100;
}
});

return Object.keys(sumByName).map((key, index) => (
<div key={index}>
{sumByName[key].toLocaleString(undefined, {
maximumFractionDigits: 0
})}{' '}
<span>
{key}
{index < Object.keys(sumByName).length - 1 && ','}&nbsp;
</span>
</div>
));
}

renderFooter() {
const { deals, totalCount } = this.props;

Expand Down
46 changes: 45 additions & 1 deletion packages/ui-cards/src/boards/components/MainActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,23 @@ type Props = {
viewType: string;
};

class MainActionBar extends React.Component<Props> {
type State = {
showDetail: boolean;
};

class MainActionBar extends React.Component<Props, State> {
static defaultProps = {
viewType: 'board',
boardText: 'Board',
pipelineText: 'Pipeline'
};

constructor(props: Props) {
super(props);

this.state = { showDetail: false };
}

renderBoards() {
const { currentBoard, boards } = this.props;
if ((currentBoard && boards.length === 1) || boards.length === 0) {
Expand Down Expand Up @@ -358,6 +368,39 @@ class MainActionBar extends React.Component<Props> {
);
};

onDetailShowHandler = () => {
this.setState(
{
showDetail: !this.state.showDetail
},
() => {
localStorage.setItem('showSalesDetail', `${this.state.showDetail}`);
const storageChangeEvent = new Event('storageChange');
window.dispatchEvent(storageChangeEvent);
}
);
};

renderSalesDetail = () => {
if (
window.location.pathname.includes('deal/board') ||
window.location.pathname.includes('deal/calendar')
) {
return (
<Button
btnStyle="link"
size="small"
icon={this.state.showDetail ? 'eye-slash' : 'eye'}
onClick={() => this.onDetailShowHandler()}
>
{this.state.showDetail ? 'Hide detail' : 'Show detail'}
</Button>
);
}

return null;
};

render() {
const {
currentBoard,
Expand Down Expand Up @@ -416,6 +459,7 @@ class MainActionBar extends React.Component<Props> {
) : null}

{this.renderVisibility()}
{this.renderSalesDetail()}
</BarItems>
);

Expand Down
75 changes: 63 additions & 12 deletions packages/ui-cards/src/boards/components/stage/Stage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ import {
IndicatorItem,
LoadingContent,
StageFooter,
StageInfo,
StageRoot,
StageTitle
} from '../../styles/stage';
import { Dropdown, OverlayTrigger, Popover } from 'react-bootstrap';
import { IItem, IOptions, IStage } from '../../types';
import { renderAmount, renderPercentedAmount } from '../../utils';

import { AddForm } from '../../containers/portable';
import { Draggable } from 'react-beautiful-dnd';
import EmptyState from '@erxes/ui/src/components/EmptyState';
import Icon from '@erxes/ui/src/components/Icon';
import ItemList from '../stage/ItemList';
import ModalTrigger from '@erxes/ui/src/components/ModalTrigger';
import { __ } from '@erxes/ui/src/utils/core';
import React from 'react';
import { Draggable } from 'react-beautiful-dnd';
import { AddForm } from '../../containers/portable';
import { IItem, IOptions, IStage } from '../../types';
import { renderAmount } from '../../utils';
import ItemList from '../stage/ItemList';
import { OverlayTrigger, Popover, Dropdown } from 'react-bootstrap';
import { Row } from '@erxes/ui-settings/src/styles';
import { __ } from '@erxes/ui/src/utils/core';

type Props = {
loadingItems: () => boolean;
Expand Down Expand Up @@ -84,8 +85,18 @@ export default class Stage extends React.Component<Props, State> {
return clearInterval(handle);
}
}, 1000);

window.addEventListener('storageChange', this.handleStorageChange);
}

componentWillUnmount() {
window.removeEventListener('storageChange', this.handleStorageChange);
}

handleStorageChange = () => {
this.forceUpdate();
};

shouldComponentUpdate(nextProps: Props, nextState: State) {
const { stage, index, length, items, loadingItems } = this.props;
const { showSortOptions } = this.state;
Expand Down Expand Up @@ -340,6 +351,49 @@ export default class Stage extends React.Component<Props, State> {
return <EmptyState icon="columns-1" text="No stage" size="small" />;
}

const probability =
stage.probability === 'Won'
? '100%'
: stage.probability === 'Lost'
? '0%'
: stage.probability;

const detail = () => {
if (
window.location.pathname.includes('deal') &&
Object.keys(stage.amount).length > 0
) {
const forecast = () => {
if (!probability) {
return null;
}

return (
<div>
<span>{__('Forecasted') + `(${probability}):`}</span>
{renderPercentedAmount(stage.amount, parseInt(probability, 10))}
</div>
);
};

return (
<StageInfo
showAll={
localStorage.getItem('showSalesDetail') === 'true' ? true : false
}
>
<div>
<span>{__('Total') + ':'}</span>
{renderAmount(stage.amount)}
</div>
{forecast()}
</StageInfo>
);
}

return null;
};

return (
<Draggable draggableId={stage._id} index={index}>
{(provided, snapshot) => (
Expand All @@ -353,10 +407,7 @@ export default class Stage extends React.Component<Props, State> {
</div>
{this.renderCtrl()}
</StageTitle>
<Row>
{renderAmount(stage.amount)}
{renderAmount(stage.unUsedAmount, false)}
</Row>
{detail()}
<Indicator>{this.renderIndicator()}</Indicator>
</Header>
<Body innerRef={this.bodyRef} onScroll={this.onScroll}>
Expand Down
1 change: 1 addition & 0 deletions packages/ui-cards/src/boards/graphql/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ const stageCommon = `
code
age
defaultTick
probability
`;

const stages = `
Expand Down
Loading

0 comments on commit 3c76fdd

Please sign in to comment.