Pārlūkot izejas kodu

redirect to login if failed request

Daniel Bohry 9 mēneši atpakaļ
vecāks
revīzija
dafbcbaf3c

+ 11 - 0
src/routes/hooks.client.js

@@ -0,0 +1,11 @@
+import { goto } from '$app/navigation';
+
+export async function handleFetch({ request, fetch }) {
+	const response = await fetch(request);
+
+	if (response.status === 401) {
+		await goto('/login');
+	}
+
+	return response;
+}

+ 3 - 2
src/routes/insights/+page.svelte

@@ -3,6 +3,7 @@
 	import { onMount } from 'svelte';
 	import { authentication } from '../store.js';
 	import { fade } from 'svelte/transition';
+	import { goto } from '$app/navigation';
 
 	let authToken;
 	let data = {};
@@ -37,8 +38,8 @@
 				data.stocks = data.stocks.sort((a, b) => a.total - b.total);
 			}
 		} catch (error) {
-			console.error('Failed to fetch portfolio:', error);
-			alert(`Error: ${error.message}`);
+			console.error('Failed to fetch insights:', error);
+			await goto('/logout');
 		} finally {
 			isLoading = false;
 		}

+ 240 - 240
src/routes/portfolio/+page.svelte

@@ -3,6 +3,7 @@
 	import { onMount } from 'svelte';
 	import { fade } from 'svelte/transition';
 	import AddStock from '../../components/AddStock.svelte';
+	import { goto } from '$app/navigation';
 
 	let portfolioId = undefined;
 	let result = [];
@@ -44,11 +45,10 @@
 			} 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');
+			await goto('/logout');
 		} finally {
 			isLoading = false;
 		}
@@ -285,16 +285,16 @@
 	<div in:fade class="table-container">
 		<table class="stock-table">
 			<thead>
-				<tr>
-					<th>Total Value</th>
-					<th>Total Assets</th>
-				</tr>
+			<tr>
+				<th>Total Value</th>
+				<th>Total Assets</th>
+			</tr>
 			</thead>
 			<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>
 		</table>
 	</div>
@@ -302,42 +302,42 @@
 	<div in:fade class="table-container">
 		<table class="stock-table">
 			<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>
+			<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>
-						<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}
+			{#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>
 		</table>
 	</div>
@@ -352,200 +352,200 @@
 {/if}
 
 <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>

+ 2 - 2
src/routes/stocks/+page.svelte

@@ -1,6 +1,7 @@
 <script>
 	import { onMount } from 'svelte';
 	import { fade } from 'svelte/transition';
+	import { goto } from '$app/navigation';
 
 	let result = [];
 	let stockQuery = '';
@@ -27,11 +28,10 @@
 			} 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 fetch:', err);
-			alert('An error occurred. Please try again.');
+			await goto('/logout');
 		}
 	}