Procházet zdrojové kódy

update stocks page

Daniel Bohry před 9 měsíci
rodič
revize
f4d10871d7

+ 110 - 117
src/components/AddStock.svelte

@@ -1,10 +1,8 @@
 <script>
 <script>
 	export let show = false;
 	export let show = false;
-	export let onClose = () => {
-	};
+	export let onClose = () => {};
 	export let onSearch = async (code) => [];
 	export let onSearch = async (code) => [];
-	export let onAddStock = (stock) => {
-	};
+	export let onAddStock = (stock) => {};
 	export let searchResults = [];
 	export let searchResults = [];
 
 
 	let stockCode = '';
 	let stockCode = '';
@@ -28,18 +26,13 @@
 							class="form-control"
 							class="form-control"
 							placeholder="stock code or name"
 							placeholder="stock code or name"
 							bind:value={stockCode}
 							bind:value={stockCode}
-							on:input={() => stockCode = stockCode.toUpperCase()}
+							on:input={() => (stockCode = stockCode.toUpperCase())}
 							autocomplete="off"
 							autocomplete="off"
 							autofocus
 							autofocus
 						/>
 						/>
 					</div>
 					</div>
 					<div class="col">
 					<div class="col">
-						<input
-							type="reset"
-							value="cancel"
-							class="btn btn-danger"
-							on:click={onClose}
-						/>
+						<input type="reset" value="cancel" class="btn btn-danger" on:click={onClose} />
 						<input type="submit" value="search" class="btn btn-primary" />
 						<input type="submit" value="search" class="btn btn-primary" />
 					</div>
 					</div>
 				</div>
 				</div>
@@ -67,110 +60,110 @@
 {/if}
 {/if}
 
 
 <style>
 <style>
-    .modal-container {
-        position: fixed;
-        top: 0;
-        left: 0;
-        width: 100%;
-        height: 100%;
-        background-color: rgba(0, 0, 0, 0.5);
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        z-index: 1000;
-    }
-
-    .modal-content {
-        background-color: white;
-        border-radius: 12px;
-        padding: 2rem;
-        width: 100%;
-        max-width: 500px;
-        animation: fadeIn 0.3s ease-in-out;
-    }
-
-    form {
-        display: flex;
-        flex-direction: column;
-        gap: 1rem;
-    }
-
-    .row {
-        display: flex;
-        flex-wrap: wrap;
-        gap: 1rem;
-    }
-
-    .col {
-        flex: 1;
-    }
-
-    input[type="text"] {
-        width: 70%;
-        padding: 0.75rem 1rem;
-        border: 1px solid #ccc;
-        border-radius: 8px;
-        font-size: 1rem;
-        outline: none;
-        transition: border 0.2s;
-    }
-
-    input[type="text"]:focus {
-        border-color: #007bff;
-    }
-
-    .btn {
-        padding: 0.6rem 1rem;
-        font-size: 1rem;
-        border: none;
-        border-radius: 8px;
-        cursor: pointer;
-        transition: background 0.2s ease-in-out;
-    }
-
-    .btn-primary {
-        background-color: #007bff;
-        color: white;
-    }
-
-    .btn-primary:hover {
-        background-color: #0056b3;
-    }
-
-    .btn-danger {
-        background-color: #dc3545;
-        color: white;
-    }
-
-    .btn-danger:hover {
-        background-color: #b02a37;
-    }
-
-    .modal-result {
-        margin-top: 1.5rem;
-    }
-
-    .list-group-item {
-        cursor: pointer;
-        transition: background 0.2s;
-    }
-
-    .list-group-item:hover {
-        background-color: #f1f1f1;
-    }
-
-    .list-group-item button {
-        margin-left: auto;
-    }
-
-    @keyframes fadeIn {
-        from {
-            opacity: 0;
-            transform: translateY(-20px);
-        }
-        to {
-            opacity: 1;
-            transform: translateY(0);
-        }
-    }
+	.modal-container {
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		background-color: rgba(0, 0, 0, 0.5);
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		z-index: 1000;
+	}
+
+	.modal-content {
+		background-color: white;
+		border-radius: 12px;
+		padding: 2rem;
+		width: 100%;
+		max-width: 500px;
+		animation: fadeIn 0.3s ease-in-out;
+	}
+
+	form {
+		display: flex;
+		flex-direction: column;
+		gap: 1rem;
+	}
+
+	.row {
+		display: flex;
+		flex-wrap: wrap;
+		gap: 1rem;
+	}
+
+	.col {
+		flex: 1;
+	}
+
+	input[type='text'] {
+		width: 70%;
+		padding: 0.75rem 1rem;
+		border: 1px solid #ccc;
+		border-radius: 8px;
+		font-size: 1rem;
+		outline: none;
+		transition: border 0.2s;
+	}
+
+	input[type='text']:focus {
+		border-color: #007bff;
+	}
+
+	.btn {
+		padding: 0.6rem 1rem;
+		font-size: 1rem;
+		border: none;
+		border-radius: 8px;
+		cursor: pointer;
+		transition: background 0.2s ease-in-out;
+	}
+
+	.btn-primary {
+		background-color: #007bff;
+		color: white;
+	}
+
+	.btn-primary:hover {
+		background-color: #0056b3;
+	}
+
+	.btn-danger {
+		background-color: #dc3545;
+		color: white;
+	}
+
+	.btn-danger:hover {
+		background-color: #b02a37;
+	}
+
+	.modal-result {
+		margin-top: 1.5rem;
+	}
+
+	.list-group-item {
+		cursor: pointer;
+		transition: background 0.2s;
+	}
+
+	.list-group-item:hover {
+		background-color: #f1f1f1;
+	}
+
+	.list-group-item button {
+		margin-left: auto;
+	}
+
+	@keyframes fadeIn {
+		from {
+			opacity: 0;
+			transform: translateY(-20px);
+		}
+		to {
+			opacity: 1;
+			transform: translateY(0);
+		}
+	}
 </style>
 </style>

+ 1 - 1
src/routes/+page.server.js

@@ -2,4 +2,4 @@ import { redirect } from '@sveltejs/kit';
 
 
 export function load() {
 export function load() {
 	throw redirect(307, '/home');
 	throw redirect(307, '/home');
-}
+}

+ 1 - 1
src/routes/login/+page.svelte

@@ -57,7 +57,7 @@
 				const result = await response.json();
 				const result = await response.json();
 				authentication.set(result);
 				authentication.set(result);
 				localStorage.setItem('authentication', JSON.stringify(result));
 				localStorage.setItem('authentication', JSON.stringify(result));
-				await goto("/home");
+				await goto('/home');
 			} else {
 			} else {
 				const error = await response.json();
 				const error = await response.json();
 				console.error('Login failed:', error);
 				console.error('Login failed:', error);

+ 263 - 264
src/routes/portfolio/+page.svelte

@@ -29,12 +29,15 @@
 
 
 	async function fetchPortfolio() {
 	async function fetchPortfolio() {
 		try {
 		try {
-			const response = await fetch(`${import.meta.env.VITE_STOCKS_HOST}/api/portfolios?currency=${currency}`, {
-				method: 'GET',
-				headers: {
-					Authorization: 'Bearer ' + authToken
+			const response = await fetch(
+				`${import.meta.env.VITE_STOCKS_HOST}/api/portfolios?currency=${currency}`,
+				{
+					method: 'GET',
+					headers: {
+						Authorization: 'Bearer ' + authToken
+					}
 				}
 				}
-			});
+			);
 
 
 			if (response.ok) {
 			if (response.ok) {
 				await update(response.json());
 				await update(response.json());
@@ -210,7 +213,7 @@
 
 
 	function updateCurrency(event) {
 	function updateCurrency(event) {
 		currency = event.target.value;
 		currency = event.target.value;
-		fetchPortfolio()
+		fetchPortfolio();
 	}
 	}
 
 
 	function handleInputChange(event) {
 	function handleInputChange(event) {
@@ -259,39 +262,39 @@
 		show={showModal}
 		show={showModal}
 		onClose={closeModal}
 		onClose={closeModal}
 		onSearch={async (code) => {
 		onSearch={async (code) => {
-		const data = await searchStock(code);
-		if (!data || data.length === 0) {
-			alert('Stock not found.');
-			return;
-		}
-		searchStockResult = data;
+			const data = await searchStock(code);
+			if (!data || data.length === 0) {
+				alert('Stock not found.');
+				return;
+			}
+			searchStockResult = data;
 
 
-		const alreadyInPortfolio = result.some((s) => s.code === data[0]?.code);
-		if (data.length === 1 && !alreadyInPortfolio) {
-			await addSelectedStock(data[0]);
-			closeModal();
-		}
-	}}
+			const alreadyInPortfolio = result.some((s) => s.code === data[0]?.code);
+			if (data.length === 1 && !alreadyInPortfolio) {
+				await addSelectedStock(data[0]);
+				closeModal();
+			}
+		}}
 		onAddStock={async (stock) => {
 		onAddStock={async (stock) => {
-		await addSelectedStock(stock);
-		closeModal();
-	}}
+			await addSelectedStock(stock);
+			closeModal();
+		}}
 		searchResults={searchStockResult}
 		searchResults={searchStockResult}
 	/>
 	/>
 
 
 	<div in:fade class="table-container">
 	<div in:fade class="table-container">
 		<table class="stock-table">
 		<table class="stock-table">
 			<thead>
 			<thead>
-			<tr>
-				<th>Total Value</th>
-				<th>Total Assets</th>
-			</tr>
+				<tr>
+					<th>Total Value</th>
+					<th>Total Assets</th>
+				</tr>
 			</thead>
 			</thead>
 			<tbody>
 			<tbody>
-			<tr>
-				<td class="code">{formatCurrency(totalValue)}</td>
-				<td class="code">{totalAssets}</td>
-			</tr>
+				<tr>
+					<td class="code">{formatCurrency(totalValue)}</td>
+					<td class="code">{totalAssets}</td>
+				</tr>
 			</tbody>
 			</tbody>
 		</table>
 		</table>
 	</div>
 	</div>
@@ -299,254 +302,250 @@
 	<div in:fade class="table-container">
 	<div in:fade class="table-container">
 		<table class="stock-table">
 		<table class="stock-table">
 			<thead>
 			<thead>
-			<tr>
-				<th>Code</th>
-				<th>Name</th>
-				<th>Qty</th>
-				<th>Price</th>
-				<th>Total</th>
-				<th>% of Portfolio</th>
-				<th scope="col"></th>
-			</tr>
-			</thead>
-			<tbody>
-			{#each result as stock}
 				<tr>
 				<tr>
-					<td class="code">{stock.code}</td>
-					<td class="name">{stock.name}</td>
-					<td class="qty-edit">
-						<form id="updateQuantity" on:submit|preventDefault={updateStockQuantity}>
-							<input type="hidden" name="code" value={stock.code} />
-							<input
-								type="number"
-								class="qty-input"
-								name="quantity"
-								value={stock.quantity}
-								on:input={handleInputChange}
-							/>
-						</form>
-					</td>
-					<td class="price">{formatCurrency(stock.price)}</td>
-					<td class="total">{formatCurrency(stock.total)}</td>
-					<td class="percent">{calculatePercentage(stock.total, totalValue)}%</td>
-					<td>
-						<button class="remove-btn" on:click={() => remove(stock.code)} title="remove"
-						></button>
-					</td>
+					<th>Code</th>
+					<th>Name</th>
+					<th>Qty</th>
+					<th>Price</th>
+					<th>Total</th>
+					<th>% of Portfolio</th>
+					<th scope="col"></th>
 				</tr>
 				</tr>
-			{/each}
+			</thead>
+			<tbody>
+				{#each result as stock}
+					<tr>
+						<td class="code">{stock.code}</td>
+						<td class="name">{stock.name}</td>
+						<td class="qty-edit">
+							<form id="updateQuantity" on:submit|preventDefault={updateStockQuantity}>
+								<input type="hidden" name="code" value={stock.code} />
+								<input
+									type="number"
+									class="qty-input"
+									name="quantity"
+									value={stock.quantity}
+									on:input={handleInputChange}
+								/>
+							</form>
+						</td>
+						<td class="price">{formatCurrency(stock.price)}</td>
+						<td class="total">{formatCurrency(stock.total)}</td>
+						<td class="percent">{calculatePercentage(stock.total, totalValue)}%</td>
+						<td>
+							<button class="remove-btn" on:click={() => remove(stock.code)} title="remove"
+							></button>
+						</td>
+					</tr>
+				{/each}
 			</tbody>
 			</tbody>
 		</table>
 		</table>
 	</div>
 	</div>
 
 
 	{#if hasChanges}
 	{#if hasChanges}
-		<button
-			class="btn btn-primary apply-changes-btn"
-			on:click={applyChanges}
-		>
+		<button class="btn btn-primary apply-changes-btn" on:click={applyChanges}>
 			Apply Changes
 			Apply Changes
 		</button>
 		</button>
 	{/if}
 	{/if}
-
 {:else}
 {:else}
 	<div>No portfolio data available.</div>
 	<div>No portfolio data available.</div>
 {/if}
 {/if}
 
 
 <style>
 <style>
-    .apply-changes-btn {
-        position: fixed;
-        bottom: 20px;
-        right: 20px;
-        z-index: 100;
-    }
-
-    .table-container {
-        margin-top: 2rem;
-        overflow-x: auto;
-        border-radius: 12px;
-        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
-    }
-
-    .stock-table {
-        width: 100%;
-        border-collapse: collapse;
-        font-family: system-ui, sans-serif;
-        background-color: #fff;
-        border-radius: 12px;
-        overflow: hidden;
-        min-width: 600px;
-    }
-
-    th,
-    td {
-        padding: 0.5rem 1rem;
-        text-align: left;
-        white-space: nowrap;
-    }
-
-    thead {
-        background-color: #f7f7f7;
-        border-bottom: 2px solid #e0e0e0;
-    }
-
-    th {
-        font-weight: 600;
-        font-size: 0.95rem;
-        color: #333;
-    }
-
-    tbody tr:nth-child(odd) {
-        background-color: #fafafa;
-    }
-
-    tbody tr:nth-child(even) {
-        background-color: #f0f4f8;
-    }
-
-    tbody tr:hover {
-        background-color: #e1ecf4;
-    }
-
-    td {
-        font-size: 0.95rem;
-        color: #555;
-        border-bottom: 1px solid #eee;
-    }
-
-    .code {
-        font-weight: 600;
-        color: #2c3e50;
-    }
-
-    .name {
-        color: #7f8c8d;
-    }
-
-    .qty-edit {
-        display: flex;
-        align-items: center;
-        gap: 0.5rem;
-    }
-
-    .qty-input {
-        width: 60px;
-        padding: 0.4rem 0.5rem;
-        font-size: 0.9rem;
-        border: 1px solid #ccc;
-        border-radius: 6px;
-        text-align: right;
-        background-color: #fff;
-        color: #333;
-        transition: border-color 0.2s ease;
-    }
-
-    .qty-input:focus {
-        outline: none;
-        border-color: #2980b9;
-    }
-
-    .remove-btn {
-        height: 15px;
-        width: 15px;
-        background-color: #e74c3c;
-        border-radius: 50%;
-        display: inline-block;
-        border: 0;
-    }
-
-    .price {
-        color: #27ae60;
-        font-weight: bold;
-    }
-
-    .total {
-        font-weight: 500;
-        color: #34495e;
-    }
-
-    .percent {
-        color: #2980b9;
-    }
-
-    @media (max-width: 768px) {
-        input[type='number'] {
-            font-size: 0.9rem;
-        }
-    }
-
-    @media (max-width: 768px) {
-        .stock-table {
-            font-size: 0.875rem;
-        }
-
-        th,
-        td {
-            padding: 0.75rem 1rem;
-        }
-    }
-
-    .button-container {
-        display: flex;
-        gap: 10px;
-        margin-bottom: 1rem;
-        align-items: center;
-    }
-
-    .order-select {
-        width: 200px;
-        padding: 0.5rem;
-        border-radius: 8px;
-        border: 1px solid #ddd;
-        font-size: 1rem;
-        background-color: #f9f9f9;
-        transition: border-color 0.3s ease;
-    }
-
-    .order-select:focus {
-        border-color: #2980b9;
-        outline: none;
-    }
-
-    input[type='number'] {
-        width: 100%;
-        font-size: 1rem;
-        border-radius: 8px;
-        border: 1px solid #ddd;
-        margin-bottom: 10px;
-        background-color: #f9f9f9;
-        color: #333;
-        transition: border-color 0.3s ease;
-    }
-
-    input[type='number']:focus {
-        border-color: #2980b9;
-        outline: none;
-    }
-
-    .btn {
-        padding: 0.6rem 1.2rem;
-        font-size: 1rem;
-        border-radius: 8px;
-        border: none;
-        cursor: pointer;
-        transition: background-color 0.3s ease;
-    }
-
-    .btn-primary {
-        background-color: #2980b9;
-        color: white;
-    }
-
-    .btn-primary:hover {
-        background-color: #3498db;
-    }
-
-    @keyframes fadeIn {
-        from {
-            opacity: 0;
-        }
-        to {
-            opacity: 1;
-        }
-    }
+	.apply-changes-btn {
+		position: fixed;
+		bottom: 20px;
+		right: 20px;
+		z-index: 100;
+	}
+
+	.table-container {
+		margin-top: 2rem;
+		overflow-x: auto;
+		border-radius: 12px;
+		box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+	}
+
+	.stock-table {
+		width: 100%;
+		border-collapse: collapse;
+		font-family: system-ui, sans-serif;
+		background-color: #fff;
+		border-radius: 12px;
+		overflow: hidden;
+		min-width: 600px;
+	}
+
+	th,
+	td {
+		padding: 0.5rem 1rem;
+		text-align: left;
+		white-space: nowrap;
+	}
+
+	thead {
+		background-color: #f7f7f7;
+		border-bottom: 2px solid #e0e0e0;
+	}
+
+	th {
+		font-weight: 600;
+		font-size: 0.95rem;
+		color: #333;
+	}
+
+	tbody tr:nth-child(odd) {
+		background-color: #fafafa;
+	}
+
+	tbody tr:nth-child(even) {
+		background-color: #f0f4f8;
+	}
+
+	tbody tr:hover {
+		background-color: #e1ecf4;
+	}
+
+	td {
+		font-size: 0.95rem;
+		color: #555;
+		border-bottom: 1px solid #eee;
+	}
+
+	.code {
+		font-weight: 600;
+		color: #2c3e50;
+	}
+
+	.name {
+		color: #7f8c8d;
+	}
+
+	.qty-edit {
+		display: flex;
+		align-items: center;
+		gap: 0.5rem;
+	}
+
+	.qty-input {
+		width: 60px;
+		padding: 0.4rem 0.5rem;
+		font-size: 0.9rem;
+		border: 1px solid #ccc;
+		border-radius: 6px;
+		text-align: right;
+		background-color: #fff;
+		color: #333;
+		transition: border-color 0.2s ease;
+	}
+
+	.qty-input:focus {
+		outline: none;
+		border-color: #2980b9;
+	}
+
+	.remove-btn {
+		height: 15px;
+		width: 15px;
+		background-color: #e74c3c;
+		border-radius: 50%;
+		display: inline-block;
+		border: 0;
+	}
+
+	.price {
+		color: #27ae60;
+		font-weight: bold;
+	}
+
+	.total {
+		font-weight: 500;
+		color: #34495e;
+	}
+
+	.percent {
+		color: #2980b9;
+	}
+
+	@media (max-width: 768px) {
+		input[type='number'] {
+			font-size: 0.9rem;
+		}
+	}
+
+	@media (max-width: 768px) {
+		.stock-table {
+			font-size: 0.875rem;
+		}
+
+		th,
+		td {
+			padding: 0.75rem 1rem;
+		}
+	}
+
+	.button-container {
+		display: flex;
+		gap: 10px;
+		margin-bottom: 1rem;
+		align-items: center;
+	}
+
+	.order-select {
+		width: 200px;
+		padding: 0.5rem;
+		border-radius: 8px;
+		border: 1px solid #ddd;
+		font-size: 1rem;
+		background-color: #f9f9f9;
+		transition: border-color 0.3s ease;
+	}
+
+	.order-select:focus {
+		border-color: #2980b9;
+		outline: none;
+	}
+
+	input[type='number'] {
+		width: 100%;
+		font-size: 1rem;
+		border-radius: 8px;
+		border: 1px solid #ddd;
+		margin-bottom: 10px;
+		background-color: #f9f9f9;
+		color: #333;
+		transition: border-color 0.3s ease;
+	}
+
+	input[type='number']:focus {
+		border-color: #2980b9;
+		outline: none;
+	}
+
+	.btn {
+		padding: 0.6rem 1.2rem;
+		font-size: 1rem;
+		border-radius: 8px;
+		border: none;
+		cursor: pointer;
+		transition: background-color 0.3s ease;
+	}
+
+	.btn-primary {
+		background-color: #2980b9;
+		color: white;
+	}
+
+	.btn-primary:hover {
+		background-color: #3498db;
+	}
+
+	@keyframes fadeIn {
+		from {
+			opacity: 0;
+		}
+		to {
+			opacity: 1;
+		}
+	}
 </style>
 </style>

+ 140 - 91
src/routes/stocks/+page.svelte

@@ -3,10 +3,11 @@
 	import { fade } from 'svelte/transition';
 	import { fade } from 'svelte/transition';
 
 
 	let result = [];
 	let result = [];
+	let stockQuery = '';
 	let isLoading = true;
 	let isLoading = true;
 
 
 	onMount(async () => {
 	onMount(async () => {
-		const stockInfo = await get(undefined);
+		const stockInfo = await get();
 		if (stockInfo) {
 		if (stockInfo) {
 			result = stockInfo;
 			result = stockInfo;
 			isLoading = false;
 			isLoading = false;
@@ -14,9 +15,7 @@
 	});
 	});
 
 
 	async function get(stock) {
 	async function get(stock) {
-		let query = stock === undefined
-			? '/api/stocks'
-			: '/api/stocks?q=' + stock;
+		let query = stock === undefined ? '/api/stocks' : '/api/stocks?q=' + stock;
 
 
 		try {
 		try {
 			const response = await fetch(import.meta.env.VITE_STOCKS_HOST + query, {
 			const response = await fetch(import.meta.env.VITE_STOCKS_HOST + query, {
@@ -31,18 +30,23 @@
 				alert('Failed to find stock info: ' + error.message);
 				alert('Failed to find stock info: ' + error.message);
 			}
 			}
 		} catch (err) {
 		} catch (err) {
-			console.error('Error during login:', err);
+			console.error('Error during fetch:', err);
 			alert('An error occurred. Please try again.');
 			alert('An error occurred. Please try again.');
 		}
 		}
 	}
 	}
 
 
+	$: filteredResults = result.filter(
+		(stock) =>
+			stock.code.toLowerCase().includes(stockQuery.toLowerCase()) ||
+			stock.name.toLowerCase().includes(stockQuery.toLowerCase())
+	);
+
 	function formatCurrency(price, currency) {
 	function formatCurrency(price, currency) {
 		return price.toLocaleString('en-US', {
 		return price.toLocaleString('en-US', {
 			style: 'currency',
 			style: 'currency',
 			currency: currency ? currency : 'USD'
 			currency: currency ? currency : 'USD'
 		});
 		});
 	}
 	}
-
 </script>
 </script>
 
 
 <svelte:head>
 <svelte:head>
@@ -53,93 +57,138 @@
 {#if isLoading}
 {#if isLoading}
 	<div in:fade>Loading...</div>
 	<div in:fade>Loading...</div>
 {:else}
 {:else}
-	<div in:fade>
-		{#if result.length !== 0}
-			<div in:fade class="result">
-				{#each result as stock}
-					<div class="card card2">
-						<div class="stock-info">
-							<div class="stock-code">{stock.code}</div>
-							<div class="stock-name">{stock.name}</div>
-						</div>
-						<div class="stock-details">
-							<div class="stock-currency">{stock.currency}</div>
-							<div class="stock-price">{formatCurrency(stock.price, stock.currency)}</div>
-						</div>
-					</div>
-				{/each}
-			</div>
-		{/if}
+	<div class="filter-card">
+		<input
+			type="text"
+			bind:value={stockQuery}
+			placeholder="Filter by name or code"
+			class="input-field"
+		/>
 	</div>
 	</div>
+
+	{#if filteredResults.length !== 0}
+		<div in:fade class="result">
+			{#each filteredResults as stock}
+				<div class="card">
+					<div class="stock-info">
+						<div class="stock-code">{stock.code}</div>
+						<div class="stock-name">{stock.name}</div>
+					</div>
+					<div class="stock-details">
+						<div class="stock-currency">{stock.currency}</div>
+						<div class="stock-price">{formatCurrency(stock.price, stock.currency)}</div>
+					</div>
+				</div>
+			{/each}
+		</div>
+	{:else}
+		<p>No matching results.</p>
+	{/if}
 {/if}
 {/if}
 
 
 <style>
 <style>
-    .result {
-        margin-top: 1rem;
-        display: flex;
-        flex-wrap: wrap;
-        gap: 2rem;
-    }
-
-    .card {
-        flex: 1 1 300px;
-    }
-
-    .card2 {
-        background-color: #fafafa;
-        border: 1px solid #eee;
-        border-radius: 12px;
-        max-width: 267px;
-    }
-
-    .card:hover {
-        transform: translateY(-10px);
-    }
-
-    .stock-info {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        margin-bottom: 10px;
-    }
-
-    .stock-code {
-        font-size: 1.5rem;
-        font-weight: bold;
-        color: #333;
-    }
-
-    .stock-name {
-        font-size: 1.1rem;
-        font-weight: normal;
-        color: #555;
-        text-align: right;
-    }
-
-    .stock-info:after {
-        content: '';
-        display: block;
-        width: 100%;
-        height: 1px;
-        background-color: #ddd;
-        margin-top: 10px;
-    }
-
-    .stock-details {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        margin-top: 15px;
-    }
-
-    .stock-currency {
-        font-size: 1rem;
-        color: #888;
-    }
-
-    .stock-price {
-        font-size: 1.5rem;
-        font-weight: bold;
-        color: #27ae60;
-    }
+	.filter-card {
+		margin: 1rem 0;
+		padding: 20px;
+		background-color: #fafafa;
+		border-radius: 12px;
+		box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
+		display: flex;
+		justify-content: center;
+	}
+
+	.input-field {
+		width: 100%;
+		max-width: 100%;
+		padding: 10px 15px;
+		font-size: 1rem;
+		border: 1px solid #ddd;
+		border-radius: 8px;
+		outline: none;
+		transition: box-shadow 0.2s;
+	}
+
+	.input-field:focus {
+		box-shadow: 0 0 0 2px #27ae60;
+		border-color: #27ae60;
+	}
+
+	.result {
+		display: flex;
+		flex-wrap: wrap;
+		gap: 2rem;
+		justify-content: center;
+		padding-bottom: 2rem;
+	}
+
+	.card {
+		background-color: #fafafa;
+		border: 1px solid #eee;
+		border-radius: 12px;
+		width: 275px;
+		height: 115px;
+		padding: 1rem;
+		box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
+		transition: transform 0.2s ease;
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+	}
+
+	.card:hover {
+		transform: translateY(-8px);
+	}
+
+	.stock-info {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		margin-bottom: 10px;
+	}
+
+	.stock-code {
+		font-size: 1.5rem;
+		font-weight: bold;
+		color: #333;
+	}
+
+	.stock-name {
+		font-size: 1.1rem;
+		color: #555;
+		text-align: right;
+	}
+
+	.stock-info:after {
+		content: '';
+		display: block;
+		width: 100%;
+		height: 1px;
+		background-color: #ddd;
+		margin-top: 10px;
+	}
+
+	.stock-details {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		margin-top: 15px;
+	}
+
+	.stock-currency {
+		font-size: 1rem;
+		color: #888;
+	}
+
+	.stock-price {
+		font-size: 1.5rem;
+		font-weight: bold;
+		color: #27ae60;
+	}
+
+	p {
+		text-align: center;
+		color: #999;
+		font-size: 1rem;
+		margin-top: 2rem;
+	}
 </style>
 </style>