|
@@ -0,0 +1,147 @@
|
|
|
|
|
+<script>
|
|
|
|
|
+ import { ArcElement, Chart, DoughnutController, Legend, Title, Tooltip } from 'chart.js';
|
|
|
|
|
+ import { onDestroy } from 'svelte';
|
|
|
|
|
+
|
|
|
|
|
+ export let data = {};
|
|
|
|
|
+ export let currency = 'USD';
|
|
|
|
|
+ export let total = 0;
|
|
|
|
|
+
|
|
|
|
|
+ let chartContainer;
|
|
|
|
|
+ let chartInstance;
|
|
|
|
|
+
|
|
|
|
|
+ function formatCurrency(value, fmtCurrency) {
|
|
|
|
|
+ return value.toLocaleString('en-US', {
|
|
|
|
|
+ style: 'currency',
|
|
|
|
|
+ currency: fmtCurrency
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const centerTextPlugin = {
|
|
|
|
|
+ id: 'centerText',
|
|
|
|
|
+ beforeDraw(chart) {
|
|
|
|
|
+ const centerTextOptions = chart.options.plugins.centerText;
|
|
|
|
|
+ if (!centerTextOptions || centerTextOptions.displayTotal === undefined || !centerTextOptions.displayCurrency) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const displayTotal = centerTextOptions.displayTotal;
|
|
|
|
|
+ const displayCurrency = centerTextOptions.displayCurrency;
|
|
|
|
|
+
|
|
|
|
|
+ const { width, height, ctx } = chart;
|
|
|
|
|
+ ctx.save();
|
|
|
|
|
+
|
|
|
|
|
+ ctx.font = 'bold 20px sans-serif';
|
|
|
|
|
+ ctx.fillStyle = '#333';
|
|
|
|
|
+ ctx.textAlign = 'center';
|
|
|
|
|
+ ctx.textBaseline = 'middle';
|
|
|
|
|
+
|
|
|
|
|
+ const text = `${formatCurrency(displayTotal, displayCurrency)}`;
|
|
|
|
|
+ ctx.fillText(text, width / 2, height / 2);
|
|
|
|
|
+
|
|
|
|
|
+ ctx.restore();
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Chart.register(ArcElement, Tooltip, Legend, Title, DoughnutController, centerTextPlugin);
|
|
|
|
|
+
|
|
|
|
|
+ $: if (chartContainer && data?.stocks) {
|
|
|
|
|
+ updateChart();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function updateChart() {
|
|
|
|
|
+ if (chartInstance) {
|
|
|
|
|
+ chartInstance.destroy();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!data || !data.stocks || data.stocks.length === 0) {
|
|
|
|
|
+ chartInstance = null;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const stockNames = data.stocks.map((stock) => stock.name);
|
|
|
|
|
+ const stockTotals = data.stocks.map((stock) => stock.total);
|
|
|
|
|
+
|
|
|
|
|
+ const chartData = {
|
|
|
|
|
+ labels: stockNames,
|
|
|
|
|
+ datasets: [
|
|
|
|
|
+ {
|
|
|
|
|
+ label: 'Stock Distribution',
|
|
|
|
|
+ data: stockTotals,
|
|
|
|
|
+ backgroundColor: [
|
|
|
|
|
+ '#1abc9c', '#2ecc71', '#3498db', '#9b59b6', '#f1c40f',
|
|
|
|
|
+ '#e67e22', '#e74c3c', '#34495e', '#16a085', '#27ae60',
|
|
|
|
|
+ '#2980b9', '#8e44ad', '#f39c12', '#d35400', '#c0392b'
|
|
|
|
|
+ ],
|
|
|
|
|
+ borderColor: '#fff',
|
|
|
|
|
+ borderWidth: 1
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const chartOptions = {
|
|
|
|
|
+ responsive: true,
|
|
|
|
|
+ maintainAspectRatio: false,
|
|
|
|
|
+ plugins: {
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ display: false
|
|
|
|
|
+ },
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ callbacks: {
|
|
|
|
|
+ label: function(tooltipItem) {
|
|
|
|
|
+ return `${tooltipItem.label}: ${formatCurrency(tooltipItem.raw, currency)}`;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ centerText: {
|
|
|
|
|
+ displayTotal: total,
|
|
|
|
|
+ displayCurrency: currency
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ chartInstance = new Chart(chartContainer, {
|
|
|
|
|
+ type: 'doughnut',
|
|
|
|
|
+ data: chartData,
|
|
|
|
|
+ options: chartOptions
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onDestroy(() => {
|
|
|
|
|
+ if (chartInstance) {
|
|
|
|
|
+ chartInstance.destroy();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<div class="chart-wrapper">
|
|
|
|
|
+ {#if data && data.stocks && data.stocks.length > 0}
|
|
|
|
|
+ <p class="chart-title">Current Portfolio Positions</p>
|
|
|
|
|
+ <canvas bind:this={chartContainer}></canvas>
|
|
|
|
|
+ {:else}
|
|
|
|
|
+ <p>No insights available.</p>
|
|
|
|
|
+ {/if}
|
|
|
|
|
+</div>
|
|
|
|
|
+
|
|
|
|
|
+<style>
|
|
|
|
|
+ .chart-wrapper {
|
|
|
|
|
+ width: 400px;
|
|
|
|
|
+ height: 400px;
|
|
|
|
|
+ max-width: 100%;
|
|
|
|
|
+ margin: auto;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ canvas {
|
|
|
|
|
+ width: 100% !important;
|
|
|
|
|
+ height: 100% !important;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .chart-title {
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ margin-bottom: 0.5rem;
|
|
|
|
|
+ font-size: 1rem;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ }
|
|
|
|
|
+</style>
|