mirror of
https://github.com/bendtherules/smallcase-portfolio-builder.git
synced 2026-08-18 22:02:56 +00:00
Complete portfolio manager functionalities
This commit is contained in:
@@ -16,6 +16,4 @@
|
|||||||
.portfolio-builder-app>.body>.title {
|
.portfolio-builder-app>.body>.title {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
color: #17BCD9;
|
color: #17BCD9;
|
||||||
|
|
||||||
padding-bottom: 1em;
|
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,8 @@ class PortfolioBuilderApp extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.addScripToPortfolio = this.addScripToPortfolio.bind(this);
|
this.addScripToPortfolio = this.addScripToPortfolio.bind(this);
|
||||||
|
this.removeScripFromPortfolio = this.removeScripFromPortfolio.bind(this);
|
||||||
|
this.addPortfolioScripCount = this.addPortfolioScripCount.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
addScripToPortfolio(scripName) {
|
addScripToPortfolio(scripName) {
|
||||||
@@ -32,7 +34,7 @@ class PortfolioBuilderApp extends Component {
|
|||||||
|
|
||||||
const oldPortfolioScripCounts = oldState.portfolioScripCounts;
|
const oldPortfolioScripCounts = oldState.portfolioScripCounts;
|
||||||
const newPortfolioScripCounts = deepCloneNaive(oldPortfolioScripCounts);
|
const newPortfolioScripCounts = deepCloneNaive(oldPortfolioScripCounts);
|
||||||
newPortfolioScripCounts[scripName] = newPortfolioScripCounts[scripName] || 0;
|
newPortfolioScripCounts[scripName] = newPortfolioScripCounts[scripName] || 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
portfolioScripPrices: newPortfolioScripPrices,
|
portfolioScripPrices: newPortfolioScripPrices,
|
||||||
@@ -61,6 +63,21 @@ class PortfolioBuilderApp extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPortfolioScripCount(scripName, incrementBy) {
|
||||||
|
this.setState((oldState) => {
|
||||||
|
if (scripName in this.state.portfolioScripPrices) {
|
||||||
|
const oldPortfolioScripCounts = oldState.portfolioScripCounts;
|
||||||
|
const newPortfolioScripCounts = deepCloneNaive(oldPortfolioScripCounts);
|
||||||
|
newPortfolioScripCounts[scripName] += incrementBy;
|
||||||
|
|
||||||
|
return {
|
||||||
|
portfolioScripCounts: newPortfolioScripCounts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
calcRemainingScripPrices() {
|
calcRemainingScripPrices() {
|
||||||
const remainingScripPrices = {};
|
const remainingScripPrices = {};
|
||||||
Object.keys(this.allScripPrices).forEach((scripName) => {
|
Object.keys(this.allScripPrices).forEach((scripName) => {
|
||||||
@@ -86,7 +103,10 @@ class PortfolioBuilderApp extends Component {
|
|||||||
/>
|
/>
|
||||||
<PortfolioManager
|
<PortfolioManager
|
||||||
portfolioScripPrices={this.state.portfolioScripPrices}
|
portfolioScripPrices={this.state.portfolioScripPrices}
|
||||||
|
portfolioScripCounts={this.state.portfolioScripCounts}
|
||||||
addScripToPortfolio={this.addScripToPortfolio}
|
addScripToPortfolio={this.addScripToPortfolio}
|
||||||
|
removeScripFromPortfolio={this.removeScripFromPortfolio}
|
||||||
|
addPortfolioScripCount={this.addPortfolioScripCount}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,4 +10,123 @@
|
|||||||
|
|
||||||
.portfolio-manager .section-title .title-tail {
|
.portfolio-manager .section-title .title-tail {
|
||||||
border-top: 1.5em solid #15569C;
|
border-top: 1.5em solid #15569C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .body {
|
||||||
|
margin-left: 1em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .portfolio-editor, .portfolio-manager .portfolio-overview {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .portfolio-editor {
|
||||||
|
width: 45%;
|
||||||
|
padding-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .portfolio-overview {
|
||||||
|
width: 55%;
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .editor-header, .portfolio-manager .editor-body {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .editor-header {
|
||||||
|
background-color: #8FB9E7;
|
||||||
|
color: white;
|
||||||
|
padding: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .editor-body {
|
||||||
|
box-shadow: 0 0 .25em 0 rgba(0, 0, 0, 0.20);
|
||||||
|
border-radius: .25em;
|
||||||
|
min-height: 5em;
|
||||||
|
padding-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .editor-body .scrip-line-item {
|
||||||
|
padding: .5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-stock, .portfolio-manager .section-price, .portfolio-manager .section-shares, .portfolio-manager .section-weight {
|
||||||
|
float: left;
|
||||||
|
padding: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-stock {
|
||||||
|
width: 35%;
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-shares {
|
||||||
|
width: 23%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-price, .portfolio-manager .section-weight {
|
||||||
|
width: 17%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-remove-btn {
|
||||||
|
float: left;
|
||||||
|
width: 8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .editor-body .section-stock {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
color: #287DCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .editor-body .section-remove-btn {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .editor-body .section-price, .portfolio-manager .editor-body .section-weight {
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager button {
|
||||||
|
position: relative;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* +,-,remove Button styles */
|
||||||
|
|
||||||
|
.portfolio-manager button .btn-text {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 1em;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-shares {
|
||||||
|
border-radius: .25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-shares>* {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
width: 33.33%;
|
||||||
|
height: 1.25em;
|
||||||
|
border: 1px solid #DDDDDD;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-shares .btn-text {
|
||||||
|
color: #287DCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolio-manager .section-shares .shares-value {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rupee styling */
|
||||||
|
.portfolio-manager .rupee-sign {
|
||||||
|
font-family: PingFangSC-Light, serif;
|
||||||
|
}
|
||||||
@@ -8,6 +8,29 @@ class PortfolioManager extends Component {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calcNetWorth() {
|
||||||
|
let netWorth = 0;
|
||||||
|
Object.keys(this.props.portfolioScripCounts).forEach((scripName) => {
|
||||||
|
const count = this.props.portfolioScripCounts[scripName];
|
||||||
|
const price = this.props.portfolioScripPrices[scripName];
|
||||||
|
netWorth += (price * count);
|
||||||
|
});
|
||||||
|
|
||||||
|
return netWorth;
|
||||||
|
}
|
||||||
|
|
||||||
|
weightInPortfolio(scripName) {
|
||||||
|
const count = this.props.portfolioScripCounts[scripName];
|
||||||
|
const price = this.props.portfolioScripPrices[scripName];
|
||||||
|
const weight = ((count * price) / this.calcNetWorth()) * 100;
|
||||||
|
|
||||||
|
if (Number.isNaN(weight)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return weight;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const portfolioScripPrices = this.props.portfolioScripPrices;
|
const portfolioScripPrices = this.props.portfolioScripPrices;
|
||||||
|
|
||||||
@@ -22,21 +45,86 @@ class PortfolioManager extends Component {
|
|||||||
>
|
>
|
||||||
<SectionTitle titleText="Manage Portfolio" />
|
<SectionTitle titleText="Manage Portfolio" />
|
||||||
|
|
||||||
<div className="drop-block" style={{ width: '100%', height: '500px', border: '1px solid black' }} />
|
<div className="body">
|
||||||
Please drop here
|
<div className="portfolio-editor">
|
||||||
<ul>
|
<div className="editor-header">
|
||||||
{
|
<div className="section-stock">
|
||||||
Object.keys(portfolioScripPrices).map((scripName) => {
|
STOCK
|
||||||
return (
|
</div>
|
||||||
<li
|
<div className="section-price">
|
||||||
key={scripName}
|
PRICE
|
||||||
>
|
</div>
|
||||||
{scripName} | {portfolioScripPrices[scripName]}
|
<div className="section-shares">
|
||||||
</li>
|
SHARES
|
||||||
);
|
</div>
|
||||||
})
|
<div className="section-weight">
|
||||||
}
|
WEIGHT
|
||||||
</ul>
|
</div>
|
||||||
|
<div className="section-remove-btn">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="clearfix" />
|
||||||
|
</div>
|
||||||
|
<div className="editor-body">
|
||||||
|
{
|
||||||
|
Object.keys(portfolioScripPrices).map((scripName) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="scrip-line-item"
|
||||||
|
key={scripName}
|
||||||
|
>
|
||||||
|
<div className="section-stock">
|
||||||
|
{scripName}
|
||||||
|
</div>
|
||||||
|
<div className="section-price">
|
||||||
|
<span className="rupee-sign">₹</span>
|
||||||
|
{Math.round(this.props.portfolioScripPrices[scripName])}
|
||||||
|
</div>
|
||||||
|
<div className="section-shares">
|
||||||
|
<button
|
||||||
|
className="shares-decrement-btn"
|
||||||
|
disabled={
|
||||||
|
this.props.portfolioScripCounts[scripName] <= 1
|
||||||
|
}
|
||||||
|
onClick={() => { this.props.addPortfolioScripCount(scripName, -1); }}
|
||||||
|
>
|
||||||
|
<span className="btn-text">-</span>
|
||||||
|
</button>
|
||||||
|
<span className="shares-value">
|
||||||
|
{this.props.portfolioScripCounts[scripName]}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
className="shares-increment-btn"
|
||||||
|
onClick={() => { this.props.addPortfolioScripCount(scripName, +1); }}
|
||||||
|
>
|
||||||
|
<span className="btn-text">+</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="section-weight">
|
||||||
|
{
|
||||||
|
Math.round(this.weightInPortfolio(scripName))
|
||||||
|
}
|
||||||
|
%
|
||||||
|
</div>
|
||||||
|
<div className="section-remove-btn">
|
||||||
|
<button onClick={() => { this.props.removeScripFromPortfolio(scripName); }}>
|
||||||
|
<span className="btn-text">-</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="clearfix" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="portfolio-overview">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="clearfix"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class StockPicker extends Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="scrip-details">
|
<div className="scrip-details">
|
||||||
<div className="scrip-price">
|
<div className="scrip-price">
|
||||||
<span className="rupee-sign">₹ </span>
|
<span className="rupee-sign">₹</span>
|
||||||
{Math.round(currentPageScripPrices[scripName])}
|
{Math.round(currentPageScripPrices[scripName])}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user