Переглянути джерело

add portfolio history chart (#2)

Daniel Bohry 3 місяців тому
батько
коміт
2174ea8d16

+ 1 - 2
src/components/CurrentPositionChart.svelte

@@ -123,8 +123,7 @@
 				},
 				datalabels: {
 					color: '#fff',
-					font: {
-					},
+					font: {},
 					formatter: (value) => {
 						const percentage = (value / total) * 100;
 						if (percentage < 3) {

+ 6 - 4
src/components/Header.svelte

@@ -31,10 +31,12 @@
 		<!-- Left: logo / placeholder -->
 		<div class="flex items-center">
 			<!-- Insert logo or branding here -->
-			<span class="text-lg font-bold text-theme-light dark:text-theme-dark"><a
-				href="/stocks"
-				class="hover:text-theme-light dark:hover:text-theme-dark transition-colors">Stocks</a
-			></span>
+			<span class="text-lg font-bold text-theme-light dark:text-theme-dark"
+				><a
+					href="/stocks"
+					class="hover:text-theme-light dark:hover:text-theme-dark transition-colors">Stocks</a
+				></span
+			>
 		</div>
 
 		<!-- Center: navigation -->

+ 10 - 2
src/components/MarketDistributionChart.svelte

@@ -51,7 +51,15 @@
 		}
 	};
 
-	Chart.register(DoughnutController, ArcElement, Tooltip, Legend, Title, centerTextPlugin, ChartDataLabels);
+	Chart.register(
+		DoughnutController,
+		ArcElement,
+		Tooltip,
+		Legend,
+		Title,
+		centerTextPlugin,
+		ChartDataLabels
+	);
 
 	const groupByMarket = (stocks) => {
 		const groups = {
@@ -113,7 +121,7 @@
 				},
 				tooltip: {
 					callbacks: {
-						label: function(tooltipItem) {
+						label: function (tooltipItem) {
 							const value = tooltipItem.raw;
 							const percentage = ((value / total) * 100).toFixed(2);
 							return `${percentage}% (${formatCurrency(value, currency)})`;

+ 274 - 0
src/components/PortfolioValueHistory.svelte

@@ -0,0 +1,274 @@
+<script>
+	import { onMount } from 'svelte';
+	import { Chart, registerables } from 'chart.js';
+	import { getRequest } from '../utils/api.js';
+	import { fade } from 'svelte/transition';
+
+	Chart.register(...registerables);
+
+	export let portfolioId;
+	export let authToken;
+	export let currency = localStorage.getItem('defaultCurrency') || 'USD';
+
+	let chartCanvas;
+	let chartInstance;
+	let isLoading = true;
+	let historyData = [];
+	let currentValue = null;
+	let valueChange = null;
+	let valueChangePercent = null;
+
+	async function fetchData() {
+		try {
+			isLoading = true;
+			const data = await getPortfolioHistory(portfolioId, authToken, currency);
+			historyData = data;
+
+			if (data.length > 0) {
+				// Sort by date to ensure proper order
+				historyData = data.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
+
+				// Calculate value change
+				const firstValue = historyData[0].totalValue;
+				const lastValue = historyData[historyData.length - 1].totalValue;
+				currentValue = lastValue;
+				valueChange = lastValue - firstValue;
+				valueChangePercent = firstValue !== 0 ? ((lastValue - firstValue) / firstValue) * 100 : 0;
+			}
+		} catch (err) {
+			console.error('Error fetching portfolio history:', err);
+		} finally {
+			isLoading = false;
+		}
+	}
+
+	async function getPortfolioHistory(portfolioId, token, currency) {
+		try {
+			const response = await getRequest(
+				`${import.meta.env.VITE_STOCKS_HOST}/api/portfolios/${portfolioId}/history?currency=${currency}`,
+				{},
+				token
+			);
+
+			if (response.ok) {
+				return await response.json();
+			} else {
+				console.error('Failed to fetch portfolio history:', response.status);
+				return [];
+			}
+		} catch (err) {
+			console.error('Error fetching portfolio history:', err);
+			return [];
+		}
+	}
+
+	function renderChart(data) {
+		if (!data || data.length === 0 || !chartCanvas) return;
+
+		const labels = data.map((item) => {
+			const date = new Date(item.createdAt);
+			const month = date.toLocaleDateString('en-US', { month: 'short' });
+			const year = date.getFullYear();
+			return `${month} ${year}`;
+		});
+
+		const values = data.map((item) => item.totalValue);
+
+		if (chartInstance) {
+			chartInstance.destroy();
+		}
+
+		const gradientFill = chartCanvas.getContext('2d').createLinearGradient(0, 0, 0, 400);
+		gradientFill.addColorStop(0, 'rgba(59, 130, 246, 0.3)');
+		gradientFill.addColorStop(1, 'rgba(59, 130, 246, 0.05)');
+
+		chartInstance = new Chart(chartCanvas, {
+			type: 'line',
+			data: {
+				labels,
+				datasets: [
+					{
+						label: `Portfolio Value (${currency})`,
+						data: values,
+						fill: true,
+						backgroundColor: gradientFill,
+						borderColor: 'rgb(59, 130, 246)',
+						borderWidth: 3,
+						pointBackgroundColor: 'rgb(59, 130, 246)',
+						pointBorderColor: 'white',
+						pointBorderWidth: 2,
+						pointRadius: 5,
+						pointHoverRadius: 7,
+						tension: 0.4
+					}
+				]
+			},
+			options: {
+				responsive: true,
+				maintainAspectRatio: false,
+				interaction: {
+					intersect: false,
+					mode: 'index'
+				},
+				scales: {
+					x: {
+						title: {
+							display: true,
+							text: 'Date',
+							color: 'rgb(107, 114, 128)',
+							font: {
+								size: 12,
+								weight: 'bold'
+							}
+						},
+						grid: {
+							color: 'rgba(107, 114, 128, 0.1)'
+						},
+						ticks: {
+							color: 'rgb(107, 114, 128)'
+						}
+					},
+					y: {
+						title: {
+							display: true,
+							text: `Value (${currency})`,
+							color: 'rgb(107, 114, 128)',
+							font: {
+								size: 12,
+								weight: 'bold'
+							}
+						},
+						grid: {
+							color: 'rgba(107, 114, 128, 0.1)'
+						},
+						ticks: {
+							color: 'rgb(107, 114, 128)',
+							callback: function (value) {
+								return value.toLocaleString('en-US', {
+									style: 'currency',
+									currency: currency,
+									minimumFractionDigits: 0,
+									maximumFractionDigits: 0
+								});
+							}
+						}
+					}
+				},
+				plugins: {
+					legend: {
+						display: false
+					},
+					tooltip: {
+						backgroundColor: 'rgba(0, 0, 0, 0.8)',
+						titleColor: 'white',
+						bodyColor: 'white',
+						borderColor: 'rgb(59, 130, 246)',
+						borderWidth: 1,
+						cornerRadius: 8,
+						displayColors: false,
+						callbacks: {
+							title: (context) => {
+								const idx = context[0].dataIndex;
+								const raw = data[idx];
+								const date = new Date(raw.createdAt);
+								return date.toLocaleDateString('en-US', {
+									year: 'numeric',
+									month: 'long',
+									day: 'numeric'
+								});
+							},
+							label: (context) => {
+								const value = context.parsed.y;
+								const totalAssets = data[context.dataIndex].totalAssets;
+								return [
+									`Portfolio Value: ${value.toLocaleString('en-US', {
+										style: 'currency',
+										currency: currency
+									})}`,
+									`Total Assets: ${totalAssets}`
+								];
+							}
+						}
+					}
+				}
+			}
+		});
+	}
+
+	// Reactive statement to fetch data when portfolioId, authToken or currency changes
+	$: if (portfolioId && authToken && currency) {
+		fetchData();
+	}
+
+	// Reactive statement to render chart when data and canvas are both ready
+	$: if (historyData.length > 0 && chartCanvas) {
+		renderChart(historyData);
+	}
+</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 if historyData.length === 0}
+	<div class="w-full 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">
+			Portfolio Value History
+		</h3>
+		<div class="text-center py-8">
+			<p class="text-gray-500 dark:text-gray-400">No portfolio history data available.</p>
+		</div>
+	</div>
+{:else}
+	<div class="w-full 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">
+			Portfolio Value History
+			{#if currentValue !== null}
+				<span class="ml-2 text-blue-500 dark:text-blue-300 text-lg font-medium">
+					({currentValue.toLocaleString('en-US', {
+						style: 'currency',
+						currency: currency
+					})}
+					{#if valueChangePercent !== null}
+						<span
+							class="{valueChangePercent >= 0
+								? 'text-green-500'
+								: 'text-red-500'} text-sm font-semibold ml-1 align-middle"
+						>
+							{valueChangePercent >= 0 ? '+' : ''}{valueChangePercent.toFixed(2)}%
+						</span>
+					{/if}
+					)
+				</span>
+			{/if}
+		</h3>
+
+		{#if valueChange !== null}
+			<div class="mb-4 text-sm text-gray-600 dark:text-gray-400">
+				<span class="font-medium">Total Change: </span>
+				<span class="{valueChange >= 0 ? 'text-green-600' : 'text-red-600'} font-semibold">
+					{valueChange >= 0 ? '+' : ''}{valueChange.toLocaleString('en-US', {
+						style: 'currency',
+						currency: currency
+					})}
+				</span>
+				<span class="ml-2 text-xs text-gray-500">
+					({historyData.length} months of data)
+				</span>
+			</div>
+		{/if}
+
+		<div class="relative h-80">
+			<canvas bind:this={chartCanvas}></canvas>
+		</div>
+	</div>
+{/if}

+ 28 - 20
src/components/StockPriceHistory.svelte

@@ -24,7 +24,11 @@
 
 	async function fetchData(range) {
 		try {
-			const res = await getRequest(`${BASE_URL}/api/stocks/${code}/history?range=${range}`, {}, null);
+			const res = await getRequest(
+				`${BASE_URL}/api/stocks/${code}/history?range=${range}`,
+				{},
+				null
+			);
 			if (!res.ok) {
 				console.error(`Failed to fetch price history: ${res.status}`);
 				return [];
@@ -104,7 +108,6 @@
 					}
 				}
 			}
-
 		});
 	}
 
@@ -158,27 +161,32 @@
 		<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)}
-			{#if priceChange !== null}
-				<span class="{priceChange >= 0 ? 'text-green-500' : 'text-red-500'} text-sm font-semibold ml-1 align-middle">
-					{priceChange >= 0 ? '+' : ''}{priceChange.toFixed(2)}%
+				<span class="ml-2 text-blue-500 dark:text-blue-300 text-lg font-medium">
+					({currentCurrency}
+					{currentPrice.toFixed(2)}
+					{#if priceChange !== null}
+						<span
+							class="{priceChange >= 0
+								? 'text-green-500'
+								: 'text-red-500'} text-sm font-semibold ml-1 align-middle"
+						>
+							{priceChange >= 0 ? '+' : ''}{priceChange.toFixed(2)}%
+						</span>
+					{/if}
+					)
 				</span>
 			{/if}
-			)
-		</span>
-			{/if}
 		</h3>
 		{#if lastUpdated}
 			<p class="text-xs text-gray-500 dark:text-gray-600 mb-4">
 				Last updated: {lastUpdated.toLocaleString('en-GB', {
-				day: '2-digit',
-				month: '2-digit',
-				year: 'numeric',
-				hour: '2-digit',
-				minute: '2-digit',
-				hour12: true
-			})}
+					day: '2-digit',
+					month: '2-digit',
+					year: 'numeric',
+					hour: '2-digit',
+					minute: '2-digit',
+					hour12: true
+				})}
 			</p>
 		{/if}
 		<canvas bind:this={chartCanvas}></canvas>
@@ -188,8 +196,8 @@
 				<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'}"
+						? '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()}
@@ -197,4 +205,4 @@
 			{/each}
 		</div>
 	</div>
-{/if}
+{/if}

+ 6 - 4
src/routes/+layout.svelte

@@ -7,11 +7,11 @@
 
 	if (typeof window !== 'undefined') {
 		fetch(`${import.meta.env.VITE_STOCKS_HOST}/actuator/info`)
-			.then(res => res.json())
-			.then(data => {
+			.then((res) => res.json())
+			.then((data) => {
 				version = data?.git?.commit?.id ?? null;
 			})
-			.catch(err => {
+			.catch((err) => {
 				console.error('Failed to fetch version:', err);
 			});
 	}
@@ -29,6 +29,8 @@
 	<footer
 		class="flex flex-col items-center justify-center py-3 text-sm text-gray-400 dark:text-gray-600"
 	>
-		<p>© {year} Lhamacorp {#if version}- {version}{/if}</p>
+		<p>
+			© {year} Lhamacorp {#if version}- {version}{/if}
+		</p>
 	</footer>
 </div>

+ 26 - 18
src/routes/portfolio/+page.svelte

@@ -3,6 +3,7 @@
 	import { onDestroy, onMount } from 'svelte';
 	import { fade } from 'svelte/transition';
 	import AddStock from '../../components/AddStock.svelte';
+	import PortfolioValueHistory from '../../components/PortfolioValueHistory.svelte';
 	import { getRequest } from '../../utils/api.js';
 	import { goto } from '$app/navigation';
 	import { browser } from '$app/environment';
@@ -382,24 +383,31 @@
 		</div>
 	{/if}
 
-	<div in:fade class="overflow-x-auto mt-6 rounded-xl shadow">
-		<table class="min-w-full bg-white dark:bg-gray-900 text-sm">
-			<thead class="bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200">
-				<tr>
-					<th class="px-4 py-3 text-left font-semibold">Total Value</th>
-					<th class="px-4 py-3 text-left font-semibold">Total Assets</th>
-				</tr>
-			</thead>
-			<tbody>
-				<tr class="border-t border-gray-200 dark:border-gray-700">
-					<td class="px-4 py-3 font-semibold text-gray-800 dark:text-gray-100"
-						>{formatCurrency(totalValue)}</td
-					>
-					<td class="px-4 py-3 font-semibold text-gray-800 dark:text-gray-100">{totalAssets}</td>
-				</tr>
-			</tbody>
-		</table>
-	</div>
+<!--	<div in:fade class="overflow-x-auto mt-6 rounded-xl shadow">-->
+<!--		<table class="min-w-full bg-white dark:bg-gray-900 text-sm">-->
+<!--			<thead class="bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200">-->
+<!--				<tr>-->
+<!--					<th class="px-4 py-3 text-left font-semibold">Total Value</th>-->
+<!--					<th class="px-4 py-3 text-left font-semibold">Total Assets</th>-->
+<!--				</tr>-->
+<!--			</thead>-->
+<!--			<tbody>-->
+<!--				<tr class="border-t border-gray-200 dark:border-gray-700">-->
+<!--					<td class="px-4 py-3 font-semibold text-gray-800 dark:text-gray-100"-->
+<!--						>{formatCurrency(totalValue)}</td-->
+<!--					>-->
+<!--					<td class="px-4 py-3 font-semibold text-gray-800 dark:text-gray-100">{totalAssets}</td>-->
+<!--				</tr>-->
+<!--			</tbody>-->
+<!--		</table>-->
+<!--	</div>-->
+
+	<!-- Portfolio Value History Chart -->
+	{#if portfolioId && authToken}
+		<div in:fade class="mt-6">
+			<PortfolioValueHistory {portfolioId} {authToken} {currency} />
+		</div>
+	{/if}
 
 	<div in:fade class="overflow-x-auto mt-6 rounded-xl shadow">
 		<table class="min-w-full bg-white dark:bg-gray-900 text-sm">

+ 9 - 2
src/routes/register/+page.svelte

@@ -21,7 +21,7 @@
 		}
 
 		if (password !== passwordConfirm) {
-			alert('Passwords don\'t match');
+			alert("Passwords don't match");
 			isSubmitting = false;
 			return;
 		}
@@ -212,7 +212,14 @@
 						viewBox="0 0 24 24"
 						class={`h-5 w-5 animate-spin text-white ${!isSubmitting ? 'hidden' : ''}`}
 					>
-						<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
+						<circle
+							class="opacity-25"
+							cx="12"
+							cy="12"
+							r="10"
+							stroke="currentColor"
+							stroke-width="4"
+						/>
 						<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8H4z" />
 					</svg>
 					<span class={isSubmitting ? 'hidden' : ''}>Register</span>

+ 109 - 109
src/routes/stocks/[code]/+page.svelte

@@ -91,115 +91,115 @@
 		</div>
 
 		<!-- Details Grid -->
-		<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Code:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg"
-				>{stockInfo.code}</span
-				>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Name:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg"
-				>{stockInfo.name}</span
-				>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Description:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
-					{stockInfo.description ?? 'N/A'}
-				</span>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Foundation:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
-					{stockInfo.foundation ?? 'N/A'}
-				</span>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">IPO:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg"
-				>{stockInfo.ipo ?? 'N/A'}</span
-				>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Exchange:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
-					{stockInfo.exchange ?? 'N/A'}
-				</span>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Headquarters:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
-					{stockInfo.headquarters ?? 'N/A'}
-				</span>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Industry:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
-					{stockInfo.industry ?? 'N/A'}
-				</span>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Company Website:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
-					{#if stockInfo.companyWebsite}
-						<a
-							href={stockInfo.companyWebsite}
-							target="_blank"
-							rel="noopener noreferrer"
-							class="underline text-blue-600 dark:text-blue-400">website</a
-						>
-					{:else}
-						N/A
-					{/if}
-				</span>
-			</div>
-
-			<div
-				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"
-			>
-				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">SEC Website:</span>
-				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">
-					{#if stockInfo.secWebsite}
-						<a
-							href={stockInfo.secWebsite}
-							target="_blank"
-							rel="noopener noreferrer"
-							class="underline text-blue-600 dark:text-blue-400">sec</a
-						>
-					{:else}
-						N/A
-					{/if}
-				</span>
-			</div>
-		</div>
+		<!--		<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">-->
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Code:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg"-->
+		<!--				>{stockInfo.code}</span-->
+		<!--				>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Name:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg"-->
+		<!--				>{stockInfo.name}</span-->
+		<!--				>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Description:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">-->
+		<!--					{stockInfo.description ?? 'N/A'}-->
+		<!--				</span>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Foundation:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">-->
+		<!--					{stockInfo.foundation ?? 'N/A'}-->
+		<!--				</span>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">IPO:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg"-->
+		<!--				>{stockInfo.ipo ?? 'N/A'}</span-->
+		<!--				>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Exchange:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">-->
+		<!--					{stockInfo.exchange ?? 'N/A'}-->
+		<!--				</span>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Headquarters:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">-->
+		<!--					{stockInfo.headquarters ?? 'N/A'}-->
+		<!--				</span>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Industry:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">-->
+		<!--					{stockInfo.industry ?? 'N/A'}-->
+		<!--				</span>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">Company Website:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">-->
+		<!--					{#if stockInfo.companyWebsite}-->
+		<!--						<a-->
+		<!--							href={stockInfo.companyWebsite}-->
+		<!--							target="_blank"-->
+		<!--							rel="noopener noreferrer"-->
+		<!--							class="underline text-blue-600 dark:text-blue-400">website</a-->
+		<!--						>-->
+		<!--					{:else}-->
+		<!--						N/A-->
+		<!--					{/if}-->
+		<!--				</span>-->
+		<!--			</div>-->
+
+		<!--			<div-->
+		<!--				class="bg-gray-50 dark:bg-gray-800 p-5 rounded-lg shadow-sm flex justify-between items-center"-->
+		<!--			>-->
+		<!--				<span class="text-gray-600 dark:text-gray-300 font-medium text-base">SEC Website:</span>-->
+		<!--				<span class="text-emerald-600 dark:text-emerald-400 font-semibold text-lg">-->
+		<!--					{#if stockInfo.secWebsite}-->
+		<!--						<a-->
+		<!--							href={stockInfo.secWebsite}-->
+		<!--							target="_blank"-->
+		<!--							rel="noopener noreferrer"-->
+		<!--							class="underline text-blue-600 dark:text-blue-400">sec</a-->
+		<!--						>-->
+		<!--					{:else}-->
+		<!--						N/A-->
+		<!--					{/if}-->
+		<!--				</span>-->
+		<!--			</div>-->
+		<!--		</div>-->
 
 		<!-- External link -->
 		<a

+ 1 - 1
src/utils/api.js

@@ -21,4 +21,4 @@ export async function getRequest(url, options = {}, token) {
 	}
 
 	return response;
-}
+}