|
|
@@ -2,23 +2,24 @@
|
|
|
import { onMount } from 'svelte';
|
|
|
import { fade } from 'svelte/transition';
|
|
|
|
|
|
- let myStocks = ['AAPL', 'AME', 'BVMF:ABEV3'];
|
|
|
let result = [];
|
|
|
+ let isLoading = true;
|
|
|
|
|
|
onMount(async () => {
|
|
|
- if (myStocks.length !== 0) {
|
|
|
- for (const code of myStocks) {
|
|
|
- const stockInfo = await get(code);
|
|
|
- if (stockInfo) {
|
|
|
- result = [...result, stockInfo[0]];
|
|
|
- }
|
|
|
- }
|
|
|
+ const stockInfo = await get(undefined);
|
|
|
+ if (stockInfo) {
|
|
|
+ result = stockInfo;
|
|
|
+ isLoading = false;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
async function get(stock) {
|
|
|
+ let query = stock === undefined
|
|
|
+ ? '/api/stocks'
|
|
|
+ : '/api/stocks?q=' + stock;
|
|
|
+
|
|
|
try {
|
|
|
- const response = await fetch(import.meta.env.VITE_STOCKS_HOST + '/api/stocks?q=' + stock, {
|
|
|
+ const response = await fetch(import.meta.env.VITE_STOCKS_HOST + query, {
|
|
|
method: 'GET'
|
|
|
});
|
|
|
|
|
|
@@ -35,25 +36,13 @@
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async function add(event) {
|
|
|
- event.preventDefault();
|
|
|
-
|
|
|
- const formData = new FormData(event.target);
|
|
|
- const stock = formData.get('stock');
|
|
|
-
|
|
|
- if (!stock) {
|
|
|
- alert('Please enter a stock code.');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const stockInfo = await get(stock);
|
|
|
-
|
|
|
- if (stockInfo) {
|
|
|
- if (result.some((item) => item.code === stockInfo[0].code)) return;
|
|
|
- result = [...result, stockInfo[0]];
|
|
|
- event.target.reset();
|
|
|
- }
|
|
|
+ function formatCurrency(price, currency) {
|
|
|
+ return price.toLocaleString('en-US', {
|
|
|
+ style: 'currency',
|
|
|
+ currency: currency ? currency : 'USD'
|
|
|
+ });
|
|
|
}
|
|
|
+
|
|
|
</script>
|
|
|
|
|
|
<svelte:head>
|
|
|
@@ -61,104 +50,96 @@
|
|
|
<meta name="description" content="Stocks" />
|
|
|
</svelte:head>
|
|
|
|
|
|
-<div in:fade>
|
|
|
- <div class="card">
|
|
|
- <form on:submit={add}>
|
|
|
- <input
|
|
|
- type="text"
|
|
|
- name="stock"
|
|
|
- placeholder="Enter a stock name or code"
|
|
|
- class="input-field"
|
|
|
- />
|
|
|
- <button type="submit" class="add-button">Add</button>
|
|
|
- </form>
|
|
|
- </div>
|
|
|
-
|
|
|
- {#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">{stock.price}</div>
|
|
|
+{#if isLoading}
|
|
|
+ <div in:fade>Loading...</div>
|
|
|
+{: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>
|
|
|
- </div>
|
|
|
- {/each}
|
|
|
- </div>
|
|
|
- {/if}
|
|
|
-</div>
|
|
|
+ {/each}
|
|
|
+ </div>
|
|
|
+ {/if}
|
|
|
+ </div>
|
|
|
+{/if}
|
|
|
|
|
|
<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;
|
|
|
- }
|
|
|
+ .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;
|
|
|
+ }
|
|
|
</style>
|