diff options
| author | Martial Simon <msimon_fr@hotmail.com> | 2025-09-15 01:07:58 +0200 |
|---|---|---|
| committer | Martial Simon <msimon_fr@hotmail.com> | 2025-09-15 01:07:58 +0200 |
| commit | 967be9e750221ab2ab783f95df79bb26d290a45e (patch) | |
| tree | 6802900a5e975f9f68b169f0f503f040056d6952 /ping/frontend/src/lib/components/input/StockSelector.svelte | |
Diffstat (limited to 'ping/frontend/src/lib/components/input/StockSelector.svelte')
| -rw-r--r-- | ping/frontend/src/lib/components/input/StockSelector.svelte | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/ping/frontend/src/lib/components/input/StockSelector.svelte b/ping/frontend/src/lib/components/input/StockSelector.svelte new file mode 100644 index 0000000..1237128 --- /dev/null +++ b/ping/frontend/src/lib/components/input/StockSelector.svelte @@ -0,0 +1,231 @@ +<script lang="ts"> + import { onMount } from 'svelte'; + import Button from '../Button.svelte'; + + let { selectedStock = $bindable('AAPL') } = $props(); + + let stocks: { + symbol: string; + shortname: string; + quoteType: string; + isYahooFinance: boolean; + }[] = $state([]); + let news: { + link: string; + title: string; + thumbnail: { resolutions: { url: string; width: number; height: number }[] }; + }[] = $state([]); + + function openDialog() { + const dialog = document.querySelector('dialog'); + const backdrop = document.getElementById('backdrop'); + // @ts-ignore + dialog.open = true; + // @ts-ignore + backdrop.style.display = 'block'; + } + + function closeDialog() { + const dialog = document.querySelector('dialog'); + const backdrop = document.getElementById('backdrop'); + // @ts-ignore + dialog.open = false; + // @ts-ignore + backdrop.style.display = 'none'; + } + + function onSearch(event: SubmitEvent) { + event.preventDefault(); + // @ts-ignore + const fd = new FormData(event.target); + const searchQuery = fd.get('search'); + + fetch(`/stocksapi/search?query=${searchQuery}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache' + } + }) + .then((response) => response.json()) + .then((data) => { + stocks = data.quotes.filter((q: any) => q.isYahooFinance); + news = data.news; + }) + .catch((error) => { + console.error('Error fetching stock data:', error); + }); + } + + onMount(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + closeDialog(); + } + }; + window.addEventListener('keydown', handleKeyDown); + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }); +</script> + +<Button onclick={openDialog}>{selectedStock}</Button> + +<dialog> + <form onsubmit={onSearch}> + <input + type="search" + name="search" + id="search" + placeholder="Recherche d'actions, ETFs, entreprises" + value={selectedStock || ''} + /> + <button type="submit" class="btn">Rechercher</button> + </form> + <div class="results"> + <div class="stocks"> + {#if stocks.length === 0} + <p>Aucune action, ETFs trouvés.</p> + {:else} + <p>{stocks.length} actions, ETFs trouvés</p> + {#each stocks as stock} + <button + class="btn stock" + onclick={() => { + selectedStock = stock.symbol; + closeDialog(); + }} + disabled={!stock.isYahooFinance} + > + <pre>{stock.symbol}</pre> + <span>{stock.shortname}</span> + <i>{stock.quoteType}</i> + </button> + {/each} + {/if} + </div> + <div class="news"> + {#if news.length === 0} + <p>Pas de news trouvés.</p> + {:else} + <p>{news.length} news trouvées</p> + {#each news as newsItem} + <a class="newsItem" href={newsItem.link}> + {#if newsItem.thumbnail?.resolutions?.length > 0} + <img + src={newsItem.thumbnail.resolutions[0].url} + alt="News Thumbnail" + width={newsItem.thumbnail.resolutions[0].width} + height={newsItem.thumbnail.resolutions[0].height} + /> + {/if} + <h1>{newsItem.title}</h1></a + > + {/each} + {/if} + </div> + </div> +</dialog> +<!-- svelte-ignore a11y_click_events_have_key_events --> +<!-- svelte-ignore a11y_no_static_element_interactions --> +<div id="backdrop" onclick={closeDialog}></div> + +<style> + dialog { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 300px; + padding: 20px; + background-color: var(--bg-primary); + border: 4px solid #ccc; + border-radius: 16px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + height: 90vh; + width: 90vw; + z-index: 1000; + color: white; + } + + #backdrop { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 999; + display: none; + } + + form { + display: flex; + gap: 10px; + } + + input[type='search'] { + width: 300px; + } + + .results { + display: flex; + flex-direction: row; + gap: 20px; + justify-content: space-evenly; + } + + .stocks, + .news { + display: flex; + flex-direction: column; + gap: 10px; + overflow-y: auto; + max-height: 70vh; + padding: 16px; + flex: 1; + } + + .stock { + display: flex; + align-items: center; + gap: 8px; + } + + .stock pre { + font-weight: bold; + padding: 8px; + background: var(--bg-primary); + border-radius: 8px; + } + + .stock span { + font-weight: bold; + color: var(--text-lime); + } + + .newsItem { + display: flex; + flex-direction: column; + background-color: var(--bg-secondary); + padding-bottom: 4px; + border-radius: 8px; + transition-duration: 0.2s; + } + + .newsItem h1 { + margin: 16px; + } + + .newsItem img { + border-radius: 8px; + width: 100%; + height: auto; + } + + .newsItem:hover { + transform: scale(1.025); + background-color: #333; + } +</style> |
