| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- <script>
- import { onMount } from 'svelte';
- import { Chart, registerables } from 'chart.js';
- import { getRequest } from '../utils/api.js';
- import { fade } from 'svelte/transition';
- import ChartDataLabels from 'chartjs-plugin-datalabels';
- Chart.register(...registerables, ChartDataLabels);
- export let code;
- let chartCanvas;
- let chartInstance;
- let selectedRange = '5d';
- let isLoading = true;
- let currentPrice = null;
- let currentCurrency = '';
- const ranges = ['5d', '30d', '6m', '1y'];
- async function fetchData(range) {
- try {
- const endpoint = `https://stocks-be.lhamacorp.com/api/stocks/${code}/history?range=${range}`;
- const res = await getRequest(endpoint, {}, null);
- if (!res.ok) {
- console.error(`Failed to fetch price history: ${res.status}`);
- return [];
- }
- return await res.json();
- } catch (err) {
- console.error('Error fetching stock history:', err);
- return [];
- } finally {
- isLoading = false;
- }
- }
- function renderChart(data) {
- const labels = data.map((item) => {
- const date = new Date(item.createdAt);
- const dd = String(date.getDate()).padStart(2, '0');
- const mm = String(date.getMonth() + 1).padStart(2, '0');
- const yyyy = date.getFullYear();
- return `${dd}/${mm}/${yyyy}`;
- });
- const prices = data.map((item) => item.price);
- if (chartInstance) {
- chartInstance.destroy();
- }
- chartInstance = new Chart(chartCanvas, {
- type: 'line',
- data: {
- labels,
- datasets: [
- {
- label: `Price (${data[0]?.currency || ''})`,
- data: prices,
- fill: false,
- borderWidth: 2
- }
- ]
- },
- options: {
- responsive: true,
- scales: {
- x: {
- title: {
- display: true,
- text: 'Date'
- }
- },
- y: {
- title: {
- display: true,
- text: 'Price'
- }
- }
- },
- plugins: {
- legend: {
- display: false
- },
- datalabels: {
- color: '#fff',
- anchor: 'end',
- align: 'top',
- font: {
- weight: 'bold'
- },
- formatter: (value) => value.toFixed(2)
- }
- }
- }
- });
- }
- async function updateChart(range) {
- selectedRange = range;
- isLoading = true;
- const history = await fetchData(range);
- isLoading = false;
- if (history.length > 0) {
- currentPrice = history[history.length - 1].price;
- currentCurrency = history[history.length - 1].currency || '';
- renderChart(history);
- }
- }
- onMount(() => {
- updateChart(selectedRange);
- });
- </script>
- {#if isLoading}
- <div in:fade class="flex justify-center items-center py-10">
- <svg
- class="animate-spin h-8 w-8 text-blue-500 dark:text-blue-300"
- xmlns="http://www.w3.org/2000/svg"
- fill="none"
- viewBox="0 0 24 24"
- >
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"
- ></circle>
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z"></path>
- </svg>
- </div>
- {:else}
- <div class="w-full mt-10 max-w-6xl mx-auto bg-white dark:bg-gray-900 p-6 rounded-xl shadow-md">
- <h3 class="text-xl font-semibold text-gray-800 dark:text-gray-100 mb-4">
- Price History
- {#if currentPrice !== null}
- <span class="ml-2 text-blue-500 dark:text-blue-300 text-lg font-medium">
- ({currentCurrency} {currentPrice.toFixed(2)})
- </span>
- {/if}
- </h3>
- <canvas bind:this={chartCanvas}></canvas>
- <div class="flex justify-center gap-4 mt-6">
- {#each ranges as range}
- <button
- class="px-4 py-2 rounded-md text-sm font-medium transition cursor-pointer
- {selectedRange === range
- ? 'bg-blue-500 text-white'
- : 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-100'}"
- on:click={() => updateChart(range)}
- >
- {range.toUpperCase()}
- </button>
- {/each}
- </div>
- </div>
- {/if}
|