Ver Fonte

Add stocks and portfolio

Daniel Bohry há 1 ano atrás
pai
commit
b0a0f27b2b

+ 10 - 2
src/routes/Header.svelte

@@ -4,8 +4,8 @@
     import {authentication} from "./store.js";
     import {onMount} from "svelte";
 
-    let username = null;
-    let profileTitle = null;
+    let username = undefined;
+    let profileTitle = undefined;
 
     const unsubscribe = authentication.subscribe(value => {
         username = value ? value.username : "";
@@ -39,6 +39,14 @@
             <li aria-current={$page.url.pathname === '/' ? 'page' : undefined}>
                 <a href="/">Home</a>
             </li>
+            {#if username !== ""}
+                <li aria-current={$page.url.pathname === '/stocks' ? 'page' : undefined}>
+                    <a href="/stocks">Stocks</a>
+                </li>
+                <li aria-current={$page.url.pathname === '/portfolio' ? 'page' : undefined}>
+                    <a href="/portfolio">Portfolio</a>
+                </li>
+            {/if}
             <li aria-current={$page.url.pathname === '/about' ? 'page' : undefined}>
                 <a href="/about">About</a>
             </li>

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

@@ -91,7 +91,7 @@
                 <form on:submit={submit}>
                     <input type="text" name="username" placeholder="username" class="input-field"/>
                     <input type="password" name="password" placeholder="password" class="input-field"/>
-                    <button type="submit" class="login-button">Login</button>
+                    <button type="submit" class="add-button">Login</button>
                     <button type="button" class="register-button" on:click={navigateToRegister}>Register</button>
                 </form>
             </div>

+ 9 - 0
src/routes/portfolio/+page.js

@@ -0,0 +1,9 @@
+import { dev } from '$app/environment';
+
+// we don't need any JS on this page, though we'll load
+// it in dev so that we get hot module replacement
+export const csr = dev;
+
+// since there's no dynamic data here, we can prerender
+// it so that it gets served as a static asset in production
+export const prerender = true;

+ 140 - 0
src/routes/portfolio/+page.svelte

@@ -0,0 +1,140 @@
+<script>
+    let result = [];
+
+    async function getPortfolio(code) {
+        try {
+            const response = await fetch(import.meta.env.VITE_STOCKS_HOST + '/api/portfolios/' + code, {
+                method: 'GET'
+            });
+
+            if (response.ok) {
+                return await response.json();
+            } else {
+                const error = await response.json();
+                console.error('Failed to find portfolio info:', error);
+                alert('Failed to find portfolio info: ' + error.message);
+            }
+        } catch (err) {
+            console.error('Failed to find portfolio info', err);
+            alert('Failed to find portfolio info');
+        }
+    }
+
+    async function search(id) {
+        const portfolio = await getPortfolio("066b47a9-46be-487f-bcc4-f35835d0ca02");
+
+        console.log(portfolio)
+
+        if (portfolio !== undefined && portfolio.stocks.length !== 0) {
+            for (const stock of portfolio.stocks) {
+                const stockInfo = stock
+                if (stockInfo) {
+                    result = [...result, stockInfo];
+                }
+            }
+        }
+    }
+
+</script>
+
+<svelte:head>
+    <title>Stocks</title>
+    <meta name="description" content="About"/>
+</svelte:head>
+
+<div>
+    <div class="card">
+        <form on:submit={search}>
+            <input type="text" name="stock" placeholder="Enter a portfolio id" class="input-field"/>
+            <button type="submit" class="search-button">Add</button>
+        </form>
+    </div>
+
+    {#if result.length !== 0}
+        <div 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 || "USD"}</div>
+                        <div class="stock-price">{stock.price}</div>
+                    </div>
+                </div>
+            {/each}
+        </div>
+    {/if}
+</div>
+
+<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;
+    }
+</style>

+ 9 - 0
src/routes/stocks/+page.js

@@ -0,0 +1,9 @@
+import { dev } from '$app/environment';
+
+// we don't need any JS on this page, though we'll load
+// it in dev so that we get hot module replacement
+export const csr = dev;
+
+// since there's no dynamic data here, we can prerender
+// it so that it gets served as a static asset in production
+export const prerender = true;

+ 158 - 0
src/routes/stocks/+page.svelte

@@ -0,0 +1,158 @@
+<script>
+    import {onMount} from "svelte";
+
+    let myStocks = ["AAPL", "AME", "BVMF:ABEV3"];
+    let result = [];
+
+    onMount(async () => {
+        if (myStocks.length !== 0) {
+            for (const code of myStocks) {
+                const stockInfo = await get(code);
+                if (stockInfo) {
+                    result = [...result, stockInfo[0]];
+                }
+            }
+        }
+    });
+
+    async function get(stock) {
+        try {
+            const response = await fetch(import.meta.env.VITE_STOCKS_HOST + '/api/stocks?q=' + stock, {
+                method: 'GET'
+            });
+
+            if (response.ok) {
+                return await response.json();
+            } else {
+                const error = await response.json();
+                console.error('Failed to find stock info:', error);
+                alert('Failed to find stock info: ' + error.message);
+            }
+        } catch (err) {
+            console.error('Error during login:', err);
+            alert('An error occurred. Please try again.');
+        }
+    }
+
+    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();
+        }
+    }
+</script>
+
+<svelte:head>
+    <title>Stocks</title>
+    <meta name="description" content="About"/>
+</svelte:head>
+
+<div>
+    <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 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>
+                    </div>
+                </div>
+            {/each}
+        </div>
+    {/if}
+</div>
+
+<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;
+    }
+</style>

+ 4 - 5
static/css/form.css

@@ -7,7 +7,6 @@
     border-radius: 12px; /* Rounded corners */
     box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); /* Subtle shadow for depth */
     padding: 20px; /* Padding inside the card */
-    width: 300px; /* Fixed width for the card */
     transition: transform 0.3s; /* Transition for hover effect */
 }
 
@@ -16,8 +15,8 @@
 }
 
 .input-field {
-    width: 90%; /* Full width for inputs */
-    padding: 12px; /* Padding for input fields */
+    width: 100%; /* Full width for inputs */
+    padding: 12px 0; /* Padding for input fields */
     margin: 10px 0; /* Margin between input fields */
     border: 1px solid #ccc; /* Light border */
     border-radius: 6px; /* Rounded corners for input fields */
@@ -43,7 +42,7 @@ button {
     transition: background-color 0.3s; /* Transition for button color */
 }
 
-.login-button {
+.add-button {
     background-color: #007bff; /* Button color */
     color: white; /* White text color */
 }
@@ -53,6 +52,6 @@ button {
     color: white; /* White text color */
 }
 
-.login-button:hover {
+.add-button:hover {
     background-color: #0056b3; /* Darker button color on hover */
 }