301 lines
10 KiB
Plaintext
301 lines
10 KiB
Plaintext
package templates
|
|
|
|
import (
|
|
"fmt"
|
|
"portfolio-tracker/internal/model"
|
|
"portfolio-tracker/internal/web/templates/components"
|
|
)
|
|
|
|
templ StockDetailsContent(stock string, data model.YahooChartResponse, portfolios []model.Portfolio) {
|
|
<div class="page-header d-print-none" aria-label="Page header">
|
|
<div class="container-fluid">
|
|
<div class="row g-2 align-items-center">
|
|
<div class="col">
|
|
<div class="page-pretitle">{ data.Chart.Result[0].Meta.Symbol }</div>
|
|
<h2 class="page-title">{ data.Chart.Result[0].Meta.LongName }</h2>
|
|
</div>
|
|
<div class="col-auto ms-auto d-print-none">
|
|
<div class="btn-list">
|
|
<span class="d-none d-sm-inline">
|
|
<a href="/portfolio" class="btn btn-outline-primary">Zum Portfolio</a>
|
|
</span>
|
|
<button type="button" class="btn btn-primary btn-5 d-none d-sm-inline-block" data-bs-toggle="modal" data-bs-target="#addToPortfolioModal">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
<path d="M12 5l0 14"></path>
|
|
<path d="M5 12l14 0"></path>
|
|
</svg>
|
|
Zu Portfolio hinzufügen
|
|
</button>
|
|
<button type="button" class="btn btn-primary btn-6 d-sm-none btn-icon" data-bs-toggle="modal" data-bs-target="#addToPortfolioModal" aria-label="Zu Portfolio hinzufügen">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|
<path d="M12 5l0 14"></path>
|
|
<path d="M5 12l14 0"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="container-fluid mt-4">
|
|
<div class="row">
|
|
<!-- Linke Spalte: Charts -->
|
|
<div class="col-md-8 col-12">
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h1 class="card-title">Kursentwicklung</h1>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="chart"></div>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h1 class="card-title">Dividenden</h1>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="chart2"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Rechte Spalte: Details -->
|
|
<div class="col-md-4 col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2 class="card-title">Details</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-unstyled">
|
|
<li><strong>Name:</strong> { data.Chart.Result[0].Meta.LongName }</li>
|
|
<li><strong>Symbol:</strong> { data.Chart.Result[0].Meta.Symbol }</li>
|
|
<li><strong>Währung:</strong> { data.Chart.Result[0].Meta.Currency }</li>
|
|
<li><strong>Börse:</strong> { data.Chart.Result[0].Meta.Exchange }</li>
|
|
<li><strong>Zeitzone:</strong> { data.Chart.Result[0].Meta.Timezone }</li>
|
|
<li><strong>Aktueller Preis:</strong> { data.Chart.Result[0].Meta.RegularMarketPrice }</li>
|
|
<li><strong>Instrumenttyp:</strong> { data.Chart.Result[0].Meta.Instrument }</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Add to Portfolio Modal -->
|
|
<div class="modal modal-blur fade" id="addToPortfolioModal" tabindex="-1" aria-labelledby="addToPortfolioModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="addToPortfolioModalLabel">{ data.Chart.Result[0].Meta.Symbol } zu Portfolio hinzufügen</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
|
|
</div>
|
|
<form method="post" action="/portfolio/transaction">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="portfolio-select" class="form-label">Portfolio</label>
|
|
<select class="form-select" id="portfolio-select" name="portfolio_id" required>
|
|
<option value="">Wählen Sie ein Portfolio</option>
|
|
for _, portfolio := range portfolios {
|
|
<option value={ fmt.Sprintf("%d", portfolio.ID) }>
|
|
{ portfolio.Name } ({ portfolio.BaseCurrency })
|
|
</option>
|
|
}
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="transaction-type" class="form-label">Transaktionstyp</label>
|
|
<select class="form-select" id="transaction-type" name="type" required>
|
|
<option value="">Wählen Sie einen Typ</option>
|
|
<option value="BUY">Kauf</option>
|
|
<option value="SELL">Verkauf</option>
|
|
<option value="DIVIDEND">Dividende</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="transaction-stock" class="form-label">Wertpapier</label>
|
|
<input type="text" class="form-control" id="transaction-stock" name="stock" value={ data.Chart.Result[0].Meta.Symbol } readonly/>
|
|
<div class="form-text">{ data.Chart.Result[0].Meta.LongName }</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="transaction-amount" class="form-label">Anzahl</label>
|
|
<input type="number" class="form-control" id="transaction-amount" name="amount" step="0.001" min="0" required/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="transaction-price" class="form-label">Preis pro Aktie</label>
|
|
<div class="input-group">
|
|
<input type="number" class="form-control" id="transaction-price" name="price" step="0.01" min="0" value={ data.Chart.Result[0].Meta.RegularMarketPrice } required/>
|
|
<span class="input-group-text">{ data.Chart.Result[0].Meta.Currency }</span>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="transaction-date" class="form-label">Datum</label>
|
|
<input type="date" class="form-control" id="transaction-date" name="date" required/>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="transaction-note" class="form-label">Notiz (optional)</label>
|
|
<textarea class="form-control" id="transaction-note" name="note" rows="2" placeholder="z.B. Grund für Kauf/Verkauf"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">Zu Portfolio hinzufügen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Inline Scripts for Charts -->
|
|
<script>
|
|
// Stock symbol for API calls
|
|
const stockSymbol = {{ data.Chart.Result[0].Meta.Symbol }};
|
|
|
|
// Set today's date as default
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
document.getElementById('transaction-date').value = today;
|
|
|
|
// First Chart: Stock Price
|
|
fetch('/api/yahoo?stock=' + encodeURIComponent(stockSymbol))
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
console.log('Stock data received:', data);
|
|
if (data.chart && data.chart.result && data.chart.result.length > 0) {
|
|
const result = data.chart.result[0];
|
|
const timestamps = result.timestamp.map(ts =>
|
|
new Date(ts * 1000).toISOString().slice(0, 10)
|
|
);
|
|
const closes = result.indicators.quote[0].close.map(v =>
|
|
v !== null ? Math.round(v * 10000) / 10000 : null
|
|
);
|
|
|
|
const options = {
|
|
chart: {
|
|
type: 'area',
|
|
height: 350,
|
|
toolbar: {
|
|
show: true
|
|
}
|
|
},
|
|
dataLabels: {
|
|
enabled: false
|
|
},
|
|
series: [{
|
|
name: 'Kurs',
|
|
data: closes
|
|
}],
|
|
xaxis: {
|
|
categories: timestamps,
|
|
type: 'datetime'
|
|
},
|
|
yaxis: {
|
|
labels: {
|
|
formatter: function(value) {
|
|
return value ? value.toFixed(2) : '';
|
|
}
|
|
}
|
|
},
|
|
tooltip: {
|
|
x: {
|
|
format: 'dd MMM yyyy'
|
|
},
|
|
y: {
|
|
formatter: function(value) {
|
|
return value ? value.toFixed(2) : '';
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const chart = new ApexCharts(document.querySelector("#chart"), options);
|
|
chart.render();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching stock data:', error);
|
|
document.querySelector("#chart").innerHTML = '<p>Fehler beim Laden der Kursdaten.</p>';
|
|
});
|
|
|
|
// Second Chart: Dividends
|
|
fetch('/api/yahoomaxdividends?stock=' + encodeURIComponent(stockSymbol))
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
console.log('Dividend data received:', data);
|
|
console.log('Data type:', typeof data);
|
|
console.log('Data length:', data ? data.length : 'no length');
|
|
|
|
if (data && Array.isArray(data) && data.length > 0) {
|
|
console.log('First dividend entry:', data[0]);
|
|
|
|
// Convert the data to the format ApexCharts expects
|
|
const dividendData = data.map(item => ({
|
|
x: new Date(item[0]),
|
|
y: item[1]
|
|
}));
|
|
|
|
console.log('Processed dividend data:', dividendData);
|
|
|
|
const options2 = {
|
|
chart: {
|
|
type: 'bar',
|
|
height: 350,
|
|
toolbar: {
|
|
show: true
|
|
}
|
|
},
|
|
dataLabels: {
|
|
enabled: false,
|
|
formatter: function(val) {
|
|
return val.toFixed(2);
|
|
}
|
|
},
|
|
series: [{
|
|
name: 'Dividende',
|
|
data: dividendData
|
|
}],
|
|
xaxis: {
|
|
type: 'datetime',
|
|
title: {
|
|
text: 'Datum'
|
|
}
|
|
},
|
|
yaxis: {
|
|
title: {
|
|
text: 'Dividende'
|
|
},
|
|
labels: {
|
|
formatter: function(value) {
|
|
return value ? value.toFixed(2) : '';
|
|
}
|
|
}
|
|
},
|
|
tooltip: {
|
|
x: {
|
|
format: 'dd MMM yyyy'
|
|
},
|
|
y: {
|
|
formatter: function(value) {
|
|
return value ? value.toFixed(2) : '';
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
const chart2 = new ApexCharts(document.querySelector("#chart2"), options2);
|
|
chart2.render();
|
|
} else {
|
|
console.log('No dividend data available or data is empty');
|
|
document.querySelector("#chart2").innerHTML = '<p>Keine Dividendendaten verfügbar für dieses Wertpapier.</p>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching dividend data:', error);
|
|
document.querySelector("#chart2").innerHTML = '<p>Fehler beim Laden der Dividendendaten.</p>';
|
|
});
|
|
});
|
|
</script>
|
|
}
|
|
|
|
templ StockDetails(authenticated bool, username string, stock string, data model.YahooChartResponse, portfolios []model.Portfolio) {
|
|
@components.PageLayout(authenticated, username, data.Chart.Result[0].Meta.LongName, StockDetailsContent(stock, data, portfolios), portfolios)
|
|
}
|