first commit
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user