Parcourir la source

fix add stock modal

Daniel Bohry il y a 9 mois
Parent
commit
b969c75870
2 fichiers modifiés avec 210 ajouts et 162 suppressions
  1. 176 0
      src/components/AddStock.svelte
  2. 34 162
      src/routes/portfolio/+page.svelte

+ 176 - 0
src/components/AddStock.svelte

@@ -0,0 +1,176 @@
+<script>
+	export let show = false;
+	export let onClose = () => {
+	};
+	export let onSearch = async (code) => [];
+	export let onAddStock = (stock) => {
+	};
+	export let searchResults = [];
+
+	let stockCode = '';
+
+	function handleSubmit(e) {
+		e.preventDefault();
+		if (stockCode.trim()) {
+			onSearch(stockCode.trim());
+		}
+	}
+</script>
+
+{#if show}
+	<div class="modal-container">
+		<div class="modal-content">
+			<form on:submit|preventDefault={handleSubmit}>
+				<div class="row">
+					<div class="col">
+						<input
+							type="text"
+							class="form-control"
+							placeholder="stock code or name"
+							bind:value={stockCode}
+							on:input={() => stockCode = stockCode.toUpperCase()}
+							autocomplete="off"
+							autofocus
+						/>
+					</div>
+					<div class="col">
+						<input
+							type="reset"
+							value="cancel"
+							class="btn btn-danger"
+							on:click={onClose}
+						/>
+						<input type="submit" value="search" class="btn btn-primary" />
+					</div>
+				</div>
+			</form>
+
+			{#if searchResults.length > 0}
+				<div class="modal-result">
+					<div class="card" style="width: 90%;">
+						<ul class="list-group list-group-flush">
+							{#each searchResults as result}
+								<li
+									class="list-group-item d-flex justify-content-between align-items-center"
+									on:click={() => onAddStock(result)}
+								>
+									({result.code}) {result.name}
+									<button class="btn btn-primary btn-sm">+</button>
+								</li>
+							{/each}
+						</ul>
+					</div>
+				</div>
+			{/if}
+		</div>
+	</div>
+{/if}
+
+<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);
+        }
+    }
+</style>

+ 34 - 162
src/routes/portfolio/+page.svelte

@@ -2,6 +2,7 @@
 	import { authentication } from '../store.js';
 	import { authentication } from '../store.js';
 	import { onMount } from 'svelte';
 	import { onMount } from 'svelte';
 	import { fade } from 'svelte/transition';
 	import { fade } from 'svelte/transition';
+	import AddStock from '../../components/AddStock.svelte';
 
 
 	let portfolioId = undefined;
 	let portfolioId = undefined;
 	let result = [];
 	let result = [];
@@ -148,7 +149,7 @@
 
 
 		if (data.length === 1 && !alreadyInPortfolio) {
 		if (data.length === 1 && !alreadyInPortfolio) {
 			await addSelectedStock(data[0]);
 			await addSelectedStock(data[0]);
-			closeOrOpenModal();
+			closeModal()
 		}
 		}
 	}
 	}
 
 
@@ -168,7 +169,7 @@
 			}
 			}
 		];
 		];
 
 
-		closeOrOpenModal();
+		closeModal();
 
 
 		await updatePortfolio(result);
 		await updatePortfolio(result);
 	}
 	}
@@ -200,9 +201,14 @@
 		updatePortfolio(result);
 		updatePortfolio(result);
 	}
 	}
 
 
-	function closeOrOpenModal() {
+	function openModal() {
 		searchStockResult = [];
 		searchStockResult = [];
-		showModal = !showModal;
+		showModal = true;
+	}
+
+	function closeModal() {
+		searchStockResult = [];
+		showModal = false;
 	}
 	}
 
 
 	function updateOrderBy(event) {
 	function updateOrderBy(event) {
@@ -237,7 +243,7 @@
 			class="btn btn-primary btn-sm"
 			class="btn btn-primary btn-sm"
 			data-toggle="modal"
 			data-toggle="modal"
 			data-target="#exampleModal"
 			data-target="#exampleModal"
-			on:click={closeOrOpenModal}
+			on:click={openModal}
 		>
 		>
 			Add
 			Add
 		</button>
 		</button>
@@ -251,54 +257,29 @@
 		</select>
 		</select>
 	</div>
 	</div>
 
 
-	{#if showModal}
-		<div class="modal-container">
-			<div class="modal-content">
-				<form on:submit|preventDefault={handleSubmit}>
-					<div class="row">
-						<div class="col">
-							<input
-								type="text"
-								class="form-control"
-								placeholder="stock code or name"
-								name="stock_code"
-								oninput="this.value = this.value.toUpperCase()"
-								autocomplete="off"
-								autofocus
-							/>
-						</div>
-						<div class="col">
-							<input
-								type="reset"
-								value="cancel"
-								class="btn btn-danger"
-								on:click={closeOrOpenModal}
-							/>
-							<input type="submit" value="search" class="btn btn-primary" />
-						</div>
-					</div>
-				</form>
-
-				{#if searchStockResult.length > 0}
-					<div class="modal-result">
-						<div class="card" style="width: 100%;">
-							<ul class="list-group list-group-flush">
-								{#each searchStockResult as result}
-									<li
-										class="list-group-item d-flex justify-content-between align-items-center"
-										on:click={addSelectedStock(result)}
-									>
-										({result.code}) {result.name}
-										<button class="btn btn-primary btn-sm">+</button>
-									</li>
-								{/each}
-							</ul>
-						</div>
-					</div>
-				{/if}
-			</div>
-		</div>
-	{/if}
+	<AddStock
+		show={showModal}
+		onClose={closeModal}
+		onSearch={async (code) => {
+		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();
+		}
+	}}
+		onAddStock={async (stock) => {
+		await addSelectedStock(stock);
+		closeModal();
+	}}
+		searchResults={searchStockResult}
+	/>
 
 
 	<div in:fade class="table-container">
 	<div in:fade class="table-container">
 		<table class="stock-table">
 		<table class="stock-table">
@@ -364,7 +345,6 @@
 {/if}
 {/if}
 
 
 <style>
 <style>
-    /* General Styles for Table (Unchanged) */
     .table-container {
     .table-container {
         margin-top: 2rem;
         margin-top: 2rem;
         overflow-x: auto;
         overflow-x: auto;
@@ -473,25 +453,10 @@
         color: #2980b9;
         color: #2980b9;
     }
     }
 
 
-    /* Responsive design */
     @media (max-width: 768px) {
     @media (max-width: 768px) {
-        .modal-content {
-            width: 90%;
-            padding: 1.5rem;
-        }
-
-        input[type='text'],
         input[type='number'] {
         input[type='number'] {
             font-size: 0.9rem;
             font-size: 0.9rem;
         }
         }
-
-        .modal-result {
-            max-height: 200px;
-        }
-
-        .list-group-item {
-            font-size: 0.9rem;
-        }
     }
     }
 
 
     @media (max-width: 768px) {
     @media (max-width: 768px) {
@@ -505,33 +470,6 @@
         }
         }
     }
     }
 
 
-    /* Modal Styling */
-    .modal-container {
-        position: fixed;
-        top: 20%;
-        left: 0;
-        right: 0;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        z-index: 9999;
-        animation: fadeIn 0.3s ease;
-    }
-
-    /* Modal content styling */
-    .modal-content {
-        background-color: #fff;
-        color: #333;
-        width: 450px;
-        height: auto;
-        max-width: 500px;
-        border-radius: 10px;
-        padding: 2rem;
-        box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
-        transition: transform 0.3s ease-in-out;
-    }
-
-    /* Button container for Add and Dropdown */
     .button-container {
     .button-container {
         display: flex;
         display: flex;
         gap: 10px;
         gap: 10px;
@@ -554,8 +492,6 @@
         outline: none;
         outline: none;
     }
     }
 
 
-    /* Input Fields (Stock code and search) */
-    input[type='text'],
     input[type='number'] {
     input[type='number'] {
         width: 100%;
         width: 100%;
         padding: 0.8rem;
         padding: 0.8rem;
@@ -568,13 +504,11 @@
         transition: border-color 0.3s ease;
         transition: border-color 0.3s ease;
     }
     }
 
 
-    input[type='text']:focus,
     input[type='number']:focus {
     input[type='number']:focus {
         border-color: #2980b9;
         border-color: #2980b9;
         outline: none;
         outline: none;
     }
     }
 
 
-    /* Button styles */
     .btn {
     .btn {
         padding: 0.6rem 1.2rem;
         padding: 0.6rem 1.2rem;
         font-size: 1rem;
         font-size: 1rem;
@@ -593,68 +527,6 @@
         background-color: #3498db;
         background-color: #3498db;
     }
     }
 
 
-    .btn-danger {
-        background-color: #e74c3c;
-        color: white;
-    }
-
-    .btn-danger:hover {
-        background-color: #c0392b;
-    }
-
-    /* Reset Button (Modal) */
-    input[type='reset'] {
-        background-color: #e74c3c;
-        color: white;
-        font-size: 1rem;
-        padding: 0.6rem 1.2rem;
-        border-radius: 8px;
-        cursor: pointer;
-        border: none;
-    }
-
-    /* Modal result items */
-    .modal-result {
-        max-width: 100%;
-        max-height: 300px;
-        overflow-y: auto;
-        padding: 0.8rem;
-        background-color: #fafafa;
-        border-radius: 10px;
-        margin-top: 1rem;
-    }
-
-    .list-group-item {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        padding: 10px;
-        border: 1px solid #ddd;
-        margin-bottom: 0.5rem;
-        border-radius: 8px;
-        background-color: #f9f9f9;
-        transition: background-color 0.3s ease;
-    }
-
-    .list-group-item:hover {
-        background-color: #f1f1f1;
-    }
-
-    .list-group-item .btn-primary {
-        background-color: #2980b9;
-        color: white;
-        border-radius: 50%;
-        height: 30px;
-        width: 30px;
-        padding: 0;
-        border: none;
-    }
-
-    .list-group-item .btn-primary:hover {
-        background-color: #3498db;
-    }
-
-    /* Modal transition */
     @keyframes fadeIn {
     @keyframes fadeIn {
         from {
         from {
             opacity: 0;
             opacity: 0;