|
|
@@ -1,6 +1,6 @@
|
|
|
<script>
|
|
|
import { authentication } from '../store.js';
|
|
|
- import { onMount } from 'svelte';
|
|
|
+ import { onDestroy, onMount } from 'svelte';
|
|
|
import { fade } from 'svelte/transition';
|
|
|
import AddStock from '../../components/AddStock.svelte';
|
|
|
import { getRequest } from '../../utils/api.js';
|
|
|
@@ -17,8 +17,22 @@
|
|
|
let orderBy = 'total';
|
|
|
let currency = 'USD';
|
|
|
let hasChanges = false;
|
|
|
+ let showDeleteConfirm = false;
|
|
|
+ let stockToDelete = null;
|
|
|
+
|
|
|
+ function handleKeyDown(event) {
|
|
|
+ if (event.key === 'Escape' && showDeleteConfirm) {
|
|
|
+ cancelDelete();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (event.key === 'Escape' && showModal) {
|
|
|
+ closeModal();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
onMount(() => {
|
|
|
+ window.addEventListener('keydown', handleKeyDown);
|
|
|
+
|
|
|
return authentication.subscribe(async (auth) => {
|
|
|
if (!auth || !auth.token) {
|
|
|
await goto('/login');
|
|
|
@@ -29,6 +43,10 @@
|
|
|
});
|
|
|
});
|
|
|
|
|
|
+ onDestroy(() => {
|
|
|
+ window.removeEventListener('keydown', handleKeyDown);
|
|
|
+ });
|
|
|
+
|
|
|
async function fetchPortfolio() {
|
|
|
try {
|
|
|
const response = await getRequest(`${import.meta.env.VITE_STOCKS_HOST}/api/portfolios?currency=${currency}`, {}, authToken);
|
|
|
@@ -214,6 +232,24 @@
|
|
|
: input;
|
|
|
}
|
|
|
|
|
|
+ function confirmDelete(code) {
|
|
|
+ stockToDelete = code;
|
|
|
+ showDeleteConfirm = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ function cancelDelete() {
|
|
|
+ stockToDelete = null;
|
|
|
+ showDeleteConfirm = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ function confirmDeleteAction() {
|
|
|
+ if (stockToDelete) {
|
|
|
+ remove(stockToDelete);
|
|
|
+ stockToDelete = null;
|
|
|
+ }
|
|
|
+ showDeleteConfirm = false;
|
|
|
+ }
|
|
|
+
|
|
|
function handleInputChange(event) {
|
|
|
const form = new FormData(event.target.closest('form'));
|
|
|
const code = form.get('code');
|
|
|
@@ -280,6 +316,15 @@
|
|
|
searchResults={searchStockResult}
|
|
|
/>
|
|
|
|
|
|
+ {#if showDeleteConfirm}
|
|
|
+ <div class="modal-confirm">
|
|
|
+ <div class="modal-actions">
|
|
|
+ <button class="btn btn-danger" on:click={confirmDeleteAction}>Confirm deletion</button>
|
|
|
+ <button class="btn btn-cancel" on:click={cancelDelete}>Cancel</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ {/if}
|
|
|
+
|
|
|
<div in:fade class="table-container">
|
|
|
<table class="stock-table">
|
|
|
<thead>
|
|
|
@@ -333,7 +378,7 @@
|
|
|
<td class="total">{formatCurrency(stock.total)}</td>
|
|
|
<td class="percent">{calculatePercentage(stock.total, totalValue)}%</td>
|
|
|
<td>
|
|
|
- <button class="remove-btn" aria-label="Delete" on:click={() => remove(stock.code)} title="remove">
|
|
|
+ <button class="remove-btn" aria-label="Delete" on:click={() => confirmDelete(stock.code)} title="remove">
|
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round"
|
|
|
stroke-linejoin="round">
|
|
|
<polyline points="3 6 5 6 21 6"></polyline>
|
|
|
@@ -498,6 +543,67 @@
|
|
|
box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.4);
|
|
|
}
|
|
|
|
|
|
+ .btn-cancel {
|
|
|
+ background-color: #bdc3c7;
|
|
|
+ color: #2c3e50;
|
|
|
+ border: none;
|
|
|
+ border-radius: 8px;
|
|
|
+ padding: 0.6rem 1.2rem;
|
|
|
+ font-size: 1rem;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background-color 0.3s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-cancel:hover {
|
|
|
+ background-color: #aeb6bf;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-cancel:focus {
|
|
|
+ outline: none;
|
|
|
+ box-shadow: 0 0 0 3px rgba(189, 195, 199, 0.5);
|
|
|
+ }
|
|
|
+
|
|
|
+ .modal-confirm {
|
|
|
+ position: fixed;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ background: white;
|
|
|
+ padding: 2rem;
|
|
|
+ border-radius: 12px;
|
|
|
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
|
|
+ z-index: 1000;
|
|
|
+ text-align: center;
|
|
|
+ max-width: 90%;
|
|
|
+ width: 400px;
|
|
|
+ animation: fadeIn 0.2s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .modal-confirm h2 {
|
|
|
+ margin-bottom: 0.5rem;
|
|
|
+ font-size: 1.25rem;
|
|
|
+ }
|
|
|
+
|
|
|
+ .modal-confirm p {
|
|
|
+ margin-bottom: 1.5rem;
|
|
|
+ color: #555;
|
|
|
+ }
|
|
|
+
|
|
|
+ .modal-actions {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 1rem;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-danger {
|
|
|
+ background-color: #e74c3c;
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-danger:hover {
|
|
|
+ background-color: #c0392b;
|
|
|
+ }
|
|
|
+
|
|
|
.price {
|
|
|
color: #27ae60;
|
|
|
font-weight: bold;
|