added first implementation of position list
This commit is contained in:
@@ -6,7 +6,7 @@ import (
|
||||
"portfolio-tracker/internal/web/templates/components"
|
||||
)
|
||||
|
||||
templ PortfolioDetailContent(portfolio model.Portfolio) {
|
||||
templ PortfolioDetailContent(portfolio model.Portfolio, positions []model.Position, positionSummary model.PositionSummary) {
|
||||
<div class="container-fluid mt-4">
|
||||
<div class="page-header">
|
||||
<div class="row g-2 align-items-center">
|
||||
@@ -55,12 +55,40 @@ templ PortfolioDetailContent(portfolio model.Portfolio) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
if len(portfolio.Activities) > 0 {
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted py-4">
|
||||
Position calculation will be implemented here
|
||||
</td>
|
||||
</tr>
|
||||
if len(positions) > 0 {
|
||||
for _, position := range positions {
|
||||
if position.IsOpen() {
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-bold">{ position.Stock }</div>
|
||||
<div class="text-muted small">{ position.Currency }</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="fw-bold">{ position.FormatShares() }</span>
|
||||
</td>
|
||||
<td>
|
||||
<span>{ position.FormatCurrency(position.AverageCostPrice) }</span>
|
||||
</td>
|
||||
<td>
|
||||
<div>{ position.FormatCurrency(position.CurrentPrice) }</div>
|
||||
<div class="text-muted small">{ position.FormatCurrency(position.CurrentValue) }</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class={ position.GetUnrealizedPLColor() }>
|
||||
<strong>{ position.FormatCurrency(position.UnrealizedPL) }</strong>
|
||||
</div>
|
||||
<div class={ position.GetUnrealizedPLColor() + " small" }>
|
||||
{ position.FormatPercentage() }
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href={ templ.URL("/details?stock=" + position.Stock) } class="btn btn-sm btn-outline-primary">
|
||||
Details
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
} else {
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted py-4">
|
||||
@@ -208,7 +236,7 @@ templ PortfolioDetailContent(portfolio model.Portfolio) {
|
||||
<h3 class="card-title">Portfolio-Zusammenfassung</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@PortfolioSummary(portfolio.Activities, portfolio.BaseCurrency)
|
||||
@PortfolioSummary(positionSummary, len(portfolio.Activities))
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mt-3">
|
||||
@@ -396,32 +424,85 @@ templ PortfolioDetailContent(portfolio model.Portfolio) {
|
||||
}
|
||||
|
||||
// Separate component for portfolio summary
|
||||
templ PortfolioSummary(activities []model.Activity, currency string) {
|
||||
templ PortfolioSummary(summary model.PositionSummary, transactionCount int) {
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="mb-3">
|
||||
<div class="text-muted">Anzahl Transaktionen</div>
|
||||
<div class="h3 mb-0">{ fmt.Sprintf("%d", len(activities)) }</div>
|
||||
<div class="h3 mb-0">{ fmt.Sprintf("%d", transactionCount) }</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="text-muted">Aktueller Portfoliowert</div>
|
||||
<div class="h2 mb-0">
|
||||
{ formatCurrency(summary.TotalValue, summary.Currency) }
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="text-muted">Gesamtwert investiert</div>
|
||||
<div class="h2 mb-0">
|
||||
{ formatTotalInvestedWithConversion(activities, currency) }
|
||||
</div>
|
||||
<div class="text-muted small">
|
||||
* = Aktueller Kurs verwendet (historischer Kurs nicht verfügbar)
|
||||
<div class="h3 mb-0">
|
||||
{ formatCurrency(summary.TotalCostBasis, summary.Currency) }
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="text-muted">Letzter Kauf</div>
|
||||
<div class="h4 mb-0 text-muted">
|
||||
{ getLastBuyDate(activities) }
|
||||
<div class="text-muted">Unrealisierte Gewinne/Verluste</div>
|
||||
<div class={ getColorClass(summary.TotalUnrealizedPL) + " h3 mb-0" }>
|
||||
{ formatCurrency(summary.TotalUnrealizedPL, summary.Currency) }
|
||||
if summary.TotalCostBasis > 0 {
|
||||
<small class="ms-2">({ formatPercentage(summary.TotalUnrealizedPL / summary.TotalCostBasis * 100) })</small>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="text-muted">Dividenden erhalten</div>
|
||||
<div class="h4 mb-0 text-success">
|
||||
{ formatCurrency(summary.TotalDividends, summary.Currency) }
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="text-muted">Gesamtrendite</div>
|
||||
<div class={ getColorClass(summary.TotalReturn) + " h3 mb-0" }>
|
||||
{ formatCurrency(summary.TotalReturn, summary.Currency) }
|
||||
if summary.TotalCostBasis > 0 {
|
||||
<small class="ms-2">({ formatPercentage(summary.TotalReturnPct) })</small>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ PortfolioDetail(authenticated bool, username string, portfolio model.Portfolio, portfolios []model.Portfolio) {
|
||||
@components.PageLayout(authenticated, username, portfolio.Name, PortfolioDetailContent(portfolio), portfolios)
|
||||
// Helper functions for formatting
|
||||
func formatCurrency(value float64, currency string) string {
|
||||
switch currency {
|
||||
case "EUR":
|
||||
return fmt.Sprintf("€%.2f", value)
|
||||
case "USD":
|
||||
return fmt.Sprintf("$%.2f", value)
|
||||
case "GBP":
|
||||
return fmt.Sprintf("£%.2f", value)
|
||||
case "CHF":
|
||||
return fmt.Sprintf("%.2f CHF", value)
|
||||
default:
|
||||
return fmt.Sprintf("%.2f %s", value, currency)
|
||||
}
|
||||
}
|
||||
|
||||
func formatPercentage(value float64) string {
|
||||
if value > 0 {
|
||||
return fmt.Sprintf("+%.2f%%", value)
|
||||
}
|
||||
return fmt.Sprintf("%.2f%%", value)
|
||||
}
|
||||
|
||||
func getColorClass(value float64) string {
|
||||
if value > 0 {
|
||||
return "text-success"
|
||||
} else if value < 0 {
|
||||
return "text-danger"
|
||||
}
|
||||
return "text-muted"
|
||||
}
|
||||
|
||||
templ PortfolioDetail(authenticated bool, username string, portfolio model.Portfolio, portfolios []model.Portfolio, positions []model.Position, positionSummary model.PositionSummary) {
|
||||
@components.PageLayout(authenticated, username, portfolio.Name, PortfolioDetailContent(portfolio, positions, positionSummary), portfolios)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user