Varie migliorie
This commit is contained in:
+136
-102
@@ -1,4 +1,4 @@
|
||||
@page
|
||||
@page
|
||||
@model AutoBidder.Pages.Account.LoginModel
|
||||
@{
|
||||
Layout = null;
|
||||
@@ -10,13 +10,28 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Login - AutoBidder</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet" />
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
--md-primary: #6750A4;
|
||||
--md-on-primary: #FFFFFF;
|
||||
--md-primary-container: #EADDFF;
|
||||
--md-on-primary-container: #21005D;
|
||||
--md-surface: #FEF7FF;
|
||||
--md-surface-container: #F3EDF7;
|
||||
--md-surface-container-low: #F7F2FA;
|
||||
--md-on-surface: #1C1B1F;
|
||||
--md-on-surface-variant: #49454F;
|
||||
--md-outline: #79747E;
|
||||
--md-outline-variant: #CAC4D0;
|
||||
--md-error: #B3261E;
|
||||
--md-error-container: #F9DEDC;
|
||||
--md-on-error-container: #410E0B;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -24,143 +39,162 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: var(--md-surface);
|
||||
font-family: 'Roboto', system-ui, -apple-system, sans-serif;
|
||||
color: var(--md-on-surface);
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
.login-surface {
|
||||
background-color: var(--md-surface-container-low);
|
||||
border-radius: 28px;
|
||||
padding: 40px 32px;
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
|
||||
max-width: 400px;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,.08), 0 4px 16px rgba(103,80,164,.1);
|
||||
}
|
||||
|
||||
.login-header {
|
||||
.login-hero {
|
||||
text-align: center;
|
||||
margin-bottom: 35px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
color: #fff;
|
||||
.login-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background-color: var(--md-primary-container);
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 16px;
|
||||
font-size: 32px;
|
||||
color: var(--md-on-primary-container);
|
||||
}
|
||||
|
||||
.login-hero h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 400;
|
||||
color: var(--md-on-surface);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
.login-hero p {
|
||||
font-size: 14px;
|
||||
color: var(--md-on-surface-variant);
|
||||
}
|
||||
|
||||
.form-floating {
|
||||
margin-bottom: 20px;
|
||||
.form-field {
|
||||
position: relative;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-floating .form-control {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
height: 55px;
|
||||
padding: 16px;
|
||||
.form-field label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--md-on-surface-variant);
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.form-floating .form-control:focus {
|
||||
background: rgba(255, 255, 255, 0.12);
|
||||
border-color: #4f46e5;
|
||||
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.25);
|
||||
color: #fff;
|
||||
.form-field input {
|
||||
width: 100%;
|
||||
background-color: var(--md-surface);
|
||||
color: var(--md-on-surface);
|
||||
border: 1px solid var(--md-outline);
|
||||
border-radius: 4px;
|
||||
padding: 14px 16px;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
transition: border-color .15s, box-shadow .15s;
|
||||
}
|
||||
|
||||
.form-floating .form-control::placeholder {
|
||||
color: transparent;
|
||||
.form-field input:focus {
|
||||
border: 2px solid var(--md-primary);
|
||||
padding: 13px 15px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.form-floating label {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
padding: 16px;
|
||||
.form-field input::placeholder { color: transparent; }
|
||||
|
||||
.form-check-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.form-floating .form-control:focus ~ label,
|
||||
.form-floating .form-control:not(:placeholder-shown) ~ label {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
.form-check-row input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
accent-color: var(--md-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-check {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-check-input {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.form-check-input:checked {
|
||||
background-color: #4f46e5;
|
||||
border-color: #4f46e5;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
.form-check-row label {
|
||||
font-size: 14px;
|
||||
color: var(--md-on-surface-variant);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
||||
background-color: var(--md-primary);
|
||||
color: var(--md-on-primary);
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
padding: 14px;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 9999px;
|
||||
padding: 0 24px;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
letter-spacing: .1px;
|
||||
cursor: pointer;
|
||||
transition: filter .2s, box-shadow .2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 30px rgba(79, 70, 229, 0.4);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-login:active {
|
||||
transform: translateY(0);
|
||||
filter: brightness(0.92);
|
||||
box-shadow: 0 2px 6px rgba(103,80,164,.35);
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: rgba(239, 68, 68, 0.15);
|
||||
border: 1px solid rgba(239, 68, 68, 0.3);
|
||||
background-color: var(--md-error-container);
|
||||
color: var(--md-on-error-container);
|
||||
border-radius: 12px;
|
||||
color: #fca5a5;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
text-align: center;
|
||||
margin-top: 25px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-top: 24px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--md-outline-variant);
|
||||
}
|
||||
|
||||
.login-footer small {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.login-footer i {
|
||||
margin-right: 5px;
|
||||
color: var(--md-on-surface-variant);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<div class="login-surface">
|
||||
<div class="login-hero">
|
||||
<div class="login-icon">
|
||||
<i class="bi bi-lightning-charge-fill"></i>
|
||||
</div>
|
||||
<h1>AutoBidder</h1>
|
||||
<p>Sistema Gestione Aste Bidoo</p>
|
||||
</div>
|
||||
@@ -175,25 +209,25 @@
|
||||
|
||||
<form method="post">
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<div class="form-floating">
|
||||
<input type="text" class="form-control" id="username" name="Username"
|
||||
placeholder="Username" value="@Model.Username" required autocomplete="username" />
|
||||
|
||||
<div class="form-field">
|
||||
<label for="username"><i class="bi bi-person"></i> Username</label>
|
||||
<input type="text" id="username" name="Username"
|
||||
placeholder="Username" value="@Model.Username" required autocomplete="username" />
|
||||
</div>
|
||||
|
||||
<div class="form-floating">
|
||||
<input type="password" class="form-control" id="password" name="Password"
|
||||
placeholder="Password" required autocomplete="current-password" />
|
||||
<div class="form-field">
|
||||
<label for="password"><i class="bi bi-lock"></i> Password</label>
|
||||
<input type="password" id="password" name="Password"
|
||||
placeholder="Password" required autocomplete="current-password" />
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="rememberMe" name="RememberMe" value="true" />
|
||||
<label class="form-check-label" for="rememberMe">Ricordami</label>
|
||||
<div class="form-check-row">
|
||||
<input type="checkbox" id="rememberMe" name="RememberMe" value="true" />
|
||||
<label for="rememberMe">Ricordami</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-login">
|
||||
<button type="submit" class="btn-login">
|
||||
<i class="bi bi-box-arrow-in-right"></i> Accedi
|
||||
</button>
|
||||
</form>
|
||||
@@ -203,4 +237,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<PageTitle>Esplora Aste - AutoBidder</PageTitle>
|
||||
|
||||
<div class="browser-container animate-fade-in p-4">
|
||||
<div class="browser-container md-page-padded animate-fade-in">
|
||||
<!-- Header -->
|
||||
<div class="d-flex align-items-center justify-content-between mb-4 flex-wrap gap-3">
|
||||
<div class="d-flex align-items-center animate-fade-in-down">
|
||||
|
||||
+71
-73
@@ -1,4 +1,4 @@
|
||||
@page "/"
|
||||
@page "/"
|
||||
@attribute [Microsoft.AspNetCore.Authorization.Authorize]
|
||||
@inject AuctionMonitor AuctionMonitor
|
||||
@inject AuctionStateService AuctionStateService
|
||||
@@ -8,87 +8,86 @@
|
||||
<PageTitle>Monitor Aste - AutoBidder</PageTitle>
|
||||
|
||||
<div class="auction-monitor-container">
|
||||
<!-- Toolbar Compatta -->
|
||||
<div class="toolbar-compact">
|
||||
<!-- Pulsanti Azioni Massiva (senza conteggi) -->
|
||||
<div class="btn-group-actions">
|
||||
<button class="action-btn success" @onclick="StartAll" title="Avvia tutte le aste">
|
||||
<i class="bi bi-play-fill"></i>
|
||||
</button>
|
||||
<button class="action-btn warning" @onclick="PauseAll" title="Metti in pausa tutte le aste">
|
||||
<i class="bi bi-pause-fill"></i>
|
||||
</button>
|
||||
<button class="action-btn secondary" @onclick="StopAll" title="Ferma tutte le aste">
|
||||
<i class="bi bi-stop-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Indicatori Stato Aste (tutti gli stati) -->
|
||||
<div class="status-indicators">
|
||||
<div class="status-pill total" title="Totale aste">
|
||||
<i class="bi bi-collection"></i>
|
||||
<span>@(auctions?.Count ?? 0)</span>
|
||||
</div>
|
||||
<div class="status-pill active" title="Aste attive">
|
||||
<!-- MD3 Toolbar -->
|
||||
<div class="md-toolbar">
|
||||
<!-- Bulk action buttons -->
|
||||
<div class="d-flex gap-2 align-items-center">
|
||||
<button class="md-icon-btn success" @onclick="StartAll" title="Avvia tutte le aste" style="background-color:var(--md-success-container);color:var(--md-on-success-container);">
|
||||
<i class="bi bi-play-fill"></i>
|
||||
</button>
|
||||
<button class="md-icon-btn warning" @onclick="PauseAll" title="Metti in pausa tutte le aste" style="background-color:var(--md-warning-container);color:var(--md-on-warning-container);">
|
||||
<i class="bi bi-pause-fill"></i>
|
||||
</button>
|
||||
<button class="md-icon-btn" @onclick="StopAll" title="Ferma tutte le aste" style="background-color:var(--md-surface-container-highest);color:var(--md-on-surface-variant);">
|
||||
<i class="bi bi-stop-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Status pills -->
|
||||
<div class="d-flex gap-2 flex-wrap align-items-center">
|
||||
<span class="md-status-pill total" title="Totale aste">
|
||||
<i class="bi bi-collection"></i>
|
||||
<span>@(auctions?.Count ?? 0)</span>
|
||||
</span>
|
||||
<span class="md-status-pill active" title="Aste attive">
|
||||
<i class="bi bi-play-circle-fill"></i>
|
||||
<span>@GetActiveAuctionsCount()</span>
|
||||
</div>
|
||||
<div class="status-pill paused" title="Aste in pausa">
|
||||
</span>
|
||||
<span class="md-status-pill paused" title="Aste in pausa">
|
||||
<i class="bi bi-pause-circle-fill"></i>
|
||||
<span>@GetPausedAuctionsCount()</span>
|
||||
</div>
|
||||
<div class="status-pill stopped" title="Aste fermate">
|
||||
</span>
|
||||
<span class="md-status-pill stopped" title="Aste fermate">
|
||||
<i class="bi bi-stop-circle-fill"></i>
|
||||
<span>@GetStoppedAuctionsCount()</span>
|
||||
</div>
|
||||
<div class="status-pill won" title="Aste vinte">
|
||||
</span>
|
||||
<span class="md-status-pill won" title="Aste vinte">
|
||||
<i class="bi bi-trophy-fill"></i>
|
||||
<span>@GetWonAuctionsCount()</span>
|
||||
</div>
|
||||
<div class="status-pill lost" title="Aste perse">
|
||||
</span>
|
||||
<span class="md-status-pill lost" title="Aste perse">
|
||||
<i class="bi bi-x-circle-fill"></i>
|
||||
<span>@GetLostAuctionsCount()</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Pulsanti Gestione -->
|
||||
<div class="btn-group-manage">
|
||||
<button class="manage-btn primary" @onclick="ShowAddAuctionDialog" title="Aggiungi nuova asta">
|
||||
|
||||
<!-- Management buttons -->
|
||||
<div class="d-flex gap-1 align-items-center ms-auto flex-wrap">
|
||||
<button class="md-icon-btn primary" @onclick="ShowAddAuctionDialog" title="Aggiungi nuova asta">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</button>
|
||||
<button class="manage-btn danger" @onclick="RemoveSelectedAuction" disabled="@(selectedAuction == null)" title="Rimuovi selezionata">
|
||||
<button class="md-icon-btn danger" @onclick="RemoveSelectedAuction" disabled="@(selectedAuction == null)" title="Rimuovi selezionata">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
<div class="manage-separator"></div>
|
||||
<button class="manage-btn outline-success" @onclick="RemoveActiveAuctions" disabled="@(GetActiveAuctionsCount() == 0)" title="Rimuovi attive">
|
||||
<div style="width:1px;height:24px;background:var(--md-outline-variant);margin:0 4px;"></div>
|
||||
<button class="md-icon-btn success" @onclick="RemoveActiveAuctions" disabled="@(GetActiveAuctionsCount() == 0)" title="Rimuovi attive">
|
||||
<i class="bi bi-play-circle"></i>
|
||||
</button>
|
||||
<button class="manage-btn outline-warning" @onclick="RemovePausedAuctions" disabled="@(GetPausedAuctionsCount() == 0)" title="Rimuovi in pausa">
|
||||
<button class="md-icon-btn warning" @onclick="RemovePausedAuctions" disabled="@(GetPausedAuctionsCount() == 0)" title="Rimuovi in pausa">
|
||||
<i class="bi bi-pause-circle"></i>
|
||||
</button>
|
||||
<button class="manage-btn outline-secondary" @onclick="RemoveStoppedAuctions" disabled="@(GetStoppedAuctionsCount() == 0)" title="Rimuovi fermate">
|
||||
<button class="md-icon-btn" @onclick="RemoveStoppedAuctions" disabled="@(GetStoppedAuctionsCount() == 0)" title="Rimuovi fermate">
|
||||
<i class="bi bi-stop-circle"></i>
|
||||
</button>
|
||||
<button class="manage-btn outline-gold" @onclick="RemoveWonAuctions" disabled="@(GetWonAuctionsCount() == 0)" title="Rimuovi vinte">
|
||||
<button class="md-icon-btn" @onclick="RemoveWonAuctions" disabled="@(GetWonAuctionsCount() == 0)" title="Rimuovi vinte" style="color:var(--md-primary);">
|
||||
<i class="bi bi-trophy"></i>
|
||||
</button>
|
||||
<button class="manage-btn outline-danger" @onclick="RemoveLostAuctions" disabled="@(GetLostAuctionsCount() == 0)" title="Rimuovi perse">
|
||||
<button class="md-icon-btn danger" @onclick="RemoveLostAuctions" disabled="@(GetLostAuctionsCount() == 0)" title="Rimuovi perse">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
</button>
|
||||
<div class="manage-separator"></div>
|
||||
<button class="manage-btn danger-fill" @onclick="RemoveAllAuctions" disabled="@((auctions?.Count ?? 0) == 0)" title="Rimuovi TUTTE">
|
||||
<div style="width:1px;height:24px;background:var(--md-outline-variant);margin:0 4px;"></div>
|
||||
<button class="md-icon-btn danger" @onclick="RemoveAllAuctions" disabled="@((auctions?.Count ?? 0) == 0)" title="Rimuovi TUTTE">
|
||||
<i class="bi bi-trash-fill"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Area Principale con Layout a Griglia -->
|
||||
<!-- Main content grid -->
|
||||
<div class="main-content-area">
|
||||
<!-- Riga Superiore: Aste + Log -->
|
||||
<div class="top-row" id="topRow">
|
||||
<!-- Pannello Aste -->
|
||||
<div class="panel panel-auctions" id="panelAuctions">
|
||||
<div class="panel-header">
|
||||
<!-- Auctions panel -->
|
||||
<div class="md-panel" id="panelAuctions">
|
||||
<div class="md-panel-header">
|
||||
<span><i class="bi bi-list-check"></i> Aste Monitorate</span>
|
||||
</div>
|
||||
@if ((auctions?.Count ?? 0) == 0)
|
||||
@@ -105,7 +104,7 @@
|
||||
<tr>
|
||||
<th class="col-stato sortable-header" @onclick='() => SortAuctionsBy("stato")'>Stato @GetSortIndicator("stato")</th>
|
||||
<th class="col-nome sortable-header" @onclick='() => SortAuctionsBy("nome")'>Nome @GetSortIndicator("nome")</th>
|
||||
<th class="col-prezzo sortable-header" @onclick='() => SortAuctionsBy("prezzo")'>€ @GetSortIndicator("prezzo")</th>
|
||||
<th class="col-prezzo sortable-header" @onclick='() => SortAuctionsBy("prezzo")'>€ @GetSortIndicator("prezzo")</th>
|
||||
<th class="col-timer sortable-header" @onclick='() => SortAuctionsBy("timer")'>Timer @GetSortIndicator("timer")</th>
|
||||
<th class="col-ultimo">Ultimo</th>
|
||||
<th class="col-click sortable-header" @onclick='() => SortAuctionsBy("puntate")'>Punt. @GetSortIndicator("puntate")</th>
|
||||
@@ -171,18 +170,18 @@
|
||||
<!-- Splitter Verticale -->
|
||||
<div class="gutter gutter-vertical" id="gutterVertical"></div>
|
||||
|
||||
<!-- Pannello Log -->
|
||||
<div class="panel panel-log" id="panelLog">
|
||||
<div class="panel-header">
|
||||
<!-- Log panel -->
|
||||
<div class="md-panel" id="panelLog">
|
||||
<div class="md-panel-header">
|
||||
<span><i class="bi bi-terminal"></i> Log</span>
|
||||
<button class="btn btn-xs btn-secondary" @onclick="ClearGlobalLog">
|
||||
<button class="md-icon-btn" @onclick="ClearGlobalLog" title="Pulisci log">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-content log-box" id="globalLogContainer" @ref="globalLogRef">
|
||||
<div class="md-panel-content md-log-box" id="globalLogContainer" @ref="globalLogRef">
|
||||
@if (globalLog.Count == 0)
|
||||
{
|
||||
<div class="text-muted"><i class="bi bi-inbox"></i> Nessun log ancora...</div>
|
||||
<div style="color:var(--md-on-surface-variant);"><i class="bi bi-inbox"></i> Nessun log ancora...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -195,18 +194,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Splitter Orizzontale -->
|
||||
<div class="gutter gutter-horizontal" id="gutterHorizontal"></div>
|
||||
|
||||
<!-- Riga Inferiore: Dettagli Asta -->
|
||||
<!-- Bottom row: Auction details -->
|
||||
<div class="bottom-row" id="bottomRow">
|
||||
<div class="panel panel-details" id="panelDetails">
|
||||
<div class="md-panel" id="panelDetails">
|
||||
@if (selectedAuction != null)
|
||||
{
|
||||
<div class="auction-details-content">
|
||||
<div class="details-header">
|
||||
<i class="bi bi-info-circle-fill"></i> @selectedAuction.Name
|
||||
<small class="text-muted">(ID: @selectedAuction.AuctionId)</small>
|
||||
<div class="md-panel-header">
|
||||
<span><i class="bi bi-info-circle-fill" style="color:var(--md-primary);"></i> @selectedAuction.Name</span>
|
||||
<small style="color:var(--md-on-surface-variant);">ID: @selectedAuction.AuctionId</small>
|
||||
</div>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
@@ -260,11 +258,11 @@
|
||||
<input type="number" class="form-control form-control-sm input-narrow" @bind="selectedAuction.BidBeforeDeadlineMs" @bind:after="SaveAuctions" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label><i class="bi bi-currency-euro"></i> Min €</label>
|
||||
<label><i class="bi bi-currency-euro"></i> Min €</label>
|
||||
<input type="number" step="0.01" class="form-control form-control-sm input-narrow" @bind="selectedAuction.MinPrice" @bind:after="SaveAuctions" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<label><i class="bi bi-currency-euro"></i> Max €</label>
|
||||
<label><i class="bi bi-currency-euro"></i> Max €</label>
|
||||
<input type="number" step="0.01" class="form-control form-control-sm input-narrow" @bind="selectedAuction.MaxPrice" @bind:after="SaveAuctions" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
@@ -317,7 +315,7 @@
|
||||
<i class="bi bi-truck"></i>
|
||||
<div>
|
||||
<small>Spedizione</small>
|
||||
<strong>€@(selectedAuction.ShippingCost?.ToString("F2") ?? "0.00")</strong>
|
||||
<strong>€@(selectedAuction.ShippingCost?.ToString("F2") ?? "0.00")</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -325,7 +323,7 @@
|
||||
<div class="calc-item">
|
||||
<i class="bi bi-currency-euro"></i>
|
||||
<span class="label">Prezzo attuale</span>
|
||||
<span class="value">€@selectedAuction.CalculatedValue.CurrentPrice.ToString("F2")</span>
|
||||
<span class="value">€@selectedAuction.CalculatedValue.CurrentPrice.ToString("F2")</span>
|
||||
</div>
|
||||
<div class="calc-item">
|
||||
<i class="bi bi-hand-index"></i>
|
||||
@@ -340,13 +338,13 @@
|
||||
<div class="calc-item">
|
||||
<i class="bi bi-cash-coin"></i>
|
||||
<span class="label">Costo puntate</span>
|
||||
<span class="value">€@selectedAuction.CalculatedValue.MyBidsCost.ToString("F2")</span>
|
||||
<span class="value">€@selectedAuction.CalculatedValue.MyBidsCost.ToString("F2")</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="totals-compact">
|
||||
<div class="total-item warning">
|
||||
<span>Costo Totale se vinci</span>
|
||||
<strong>€@selectedAuction.CalculatedValue.TotalCostIfWin.ToString("F2")</strong>
|
||||
<strong>€@selectedAuction.CalculatedValue.TotalCostIfWin.ToString("F2")</strong>
|
||||
</div>
|
||||
<div class="total-item @(selectedAuction.CalculatedValue.Savings > 0 ? "success" : "danger")">
|
||||
<span>
|
||||
@@ -400,7 +398,7 @@
|
||||
{
|
||||
<tr class="@(bid.IsMyBid ? "my-bid-row" : "")">
|
||||
<td>@if (bid.IsMyBid){<strong class="text-success">@bid.Username</strong><span class="badge bg-success ms-1">TU</span>}else{@bid.Username}</td>
|
||||
<td class="fw-bold">€@bid.PriceFormatted</td>
|
||||
<td class="fw-bold">€@bid.PriceFormatted</td>
|
||||
<td class="text-muted small">@bid.TimeFormatted</td>
|
||||
<td><span class="badge bg-secondary">@bid.BidType</span></td>
|
||||
</tr>
|
||||
@@ -499,8 +497,8 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="details-placeholder">
|
||||
<i class="bi bi-arrow-up"></i>
|
||||
<div class="details-placeholder" style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;padding:32px;color:var(--md-on-surface-variant);gap:8px;">
|
||||
<i class="bi bi-arrow-up" style="font-size:2rem;color:var(--md-primary);"></i>
|
||||
<p>Seleziona un'asta per visualizzare i dettagli</p>
|
||||
</div>
|
||||
}
|
||||
@@ -512,7 +510,7 @@
|
||||
<!-- Modal Aggiungi Asta -->
|
||||
@if (showAddDialog)
|
||||
{
|
||||
<div class="modal show d-block" tabindex="-1" style="background: rgba(0,0,0,0.5);">
|
||||
<div class="modal show d-block" tabindex="-1" style="background: rgba(0,0,0,0.3);">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@page "/settings"
|
||||
@page "/settings"
|
||||
@attribute [Microsoft.AspNetCore.Authorization.Authorize]
|
||||
@inject SessionService SessionService
|
||||
@inject AuctionMonitor AuctionMonitor
|
||||
@@ -7,14 +7,14 @@
|
||||
|
||||
<PageTitle>Impostazioni - AutoBidder</PageTitle>
|
||||
|
||||
<div class="settings-container px-3 px-md-4 py-3">
|
||||
<div class="d-flex align-items-center gap-3 mb-3">
|
||||
<i class="bi bi-gear-fill text-primary" style="font-size: 2rem;"></i>
|
||||
<div>
|
||||
<h2 class="mb-0 fw-bold">Impostazioni</h2>
|
||||
<small class="text-muted">Configura sessione, comportamento aste e limiti.</small>
|
||||
</div>
|
||||
<div class="settings-container md-page-padded">
|
||||
<div class="md-toolbar mb-3" style="border-radius:var(--md-shape-medium);border:none;background:transparent;padding-left:0;">
|
||||
<i class="bi bi-gear-fill" style="font-size:2rem;color:var(--md-primary);"></i>
|
||||
<div>
|
||||
<h2 class="mb-0 md-headline-small">Impostazioni</h2>
|
||||
<small style="color:var(--md-on-surface-variant);">Configura sessione, comportamento aste e limiti.</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion" id="settingsAccordion">
|
||||
<!-- SESSIONE BIDOO -->
|
||||
@@ -162,7 +162,7 @@
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label fw-bold"><i class="bi bi-hourglass-split"></i> Intervallo Ticker (ms)</label>
|
||||
<input type="number" class="form-control" @bind="settings.TickerIntervalMs" min="10" max="500" />
|
||||
<div class="form-text">Più basso = più preciso ma più CPU. Consigliato: 50-100ms</div>
|
||||
<div class="form-text">Più basso = più preciso ma più CPU. Consigliato: 50-100ms</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label fw-bold"><i class="bi bi-funnel"></i> Soglia controllo strategie (ms)</label>
|
||||
@@ -197,7 +197,7 @@
|
||||
<div class="form-text">0 = illimitati</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label fw-bold"><i class="bi bi-currency-euro"></i> Prezzo minimo (€)</label>
|
||||
<label class="form-label fw-bold"><i class="bi bi-currency-euro"></i> Prezzo minimo (€)</label>
|
||||
<div class="input-group">
|
||||
<input type="number" step="0.01" class="form-control" @bind="settings.DefaultMinPrice" />
|
||||
<button class="btn btn-outline-primary" @onclick="() => ApplySingleSettingToAll(nameof(settings.DefaultMinPrice))"
|
||||
@@ -209,7 +209,7 @@
|
||||
<div class="form-text">0 = punta a qualsiasi prezzo. Il prezzo deve essere ? a questo valore per puntare.</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label fw-bold"><i class="bi bi-currency-euro"></i> Prezzo massimo (€)</label>
|
||||
<label class="form-label fw-bold"><i class="bi bi-currency-euro"></i> Prezzo massimo (€)</label>
|
||||
<div class="input-group">
|
||||
<input type="number" step="0.01" class="form-control" @bind="settings.DefaultMaxPrice" />
|
||||
<button class="btn btn-outline-primary" @onclick="() => ApplySingleSettingToAll(nameof(settings.DefaultMaxPrice))"
|
||||
@@ -221,19 +221,19 @@
|
||||
<div class="form-text">0 = nessun limite. Se il prezzo supera questo valore, SMETTE di puntare.</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label fw-bold"><i class="bi bi-database-gear"></i> Priorità limiti nuove aste</label>
|
||||
<label class="form-label fw-bold"><i class="bi bi-database-gear"></i> Priorità limiti nuove aste</label>
|
||||
<select class="form-select" @bind="settings.NewAuctionLimitsPriority">
|
||||
<option value="ProductStats">Usa limiti salvati nelle statistiche prodotto</option>
|
||||
<option value="GlobalDefaults">Usa sempre limiti globali</option>
|
||||
</select>
|
||||
<div class="form-text">
|
||||
Se "Statistiche prodotto", quando aggiungi un'asta di un prodotto già salvato, verranno usati i limiti personalizzati delle statistiche invece di quelli globali.
|
||||
Se "Statistiche prodotto", quando aggiungi un'asta di un prodotto già salvato, verranno usati i limiti personalizzati delle statistiche invece di quelli globali.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label fw-bold"><i class="bi bi-shield-check"></i> Puntate minime da mantenere</label>
|
||||
<input type="number" class="form-control" @bind="settings.MinimumRemainingBids" />
|
||||
<div class="form-text">Questa è un'impostazione globale</div>
|
||||
<div class="form-text">Questa è un'impostazione globale</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -263,7 +263,7 @@
|
||||
<div class="alert alert-info border-0 shadow-sm mb-4">
|
||||
<i class="bi bi-info-circle me-2"></i>
|
||||
<strong>Nota:</strong> Le strategie decidono <strong>SE</strong> puntare, non <strong>QUANDO</strong>.
|
||||
Il timing è controllato solo dall'impostazione "Anticipo puntata" nelle Impostazioni Predefinite.
|
||||
Il timing è controllato solo dall'impostazione "Anticipo puntata" nelle Impostazioni Predefinite.
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold mb-3"><i class="bi bi-thermometer-half"></i> Rilevamento Competizione</h6>
|
||||
@@ -346,12 +346,12 @@
|
||||
<input type="checkbox" class="form-check-input" id="probabilistic" @bind="settings.ProbabilisticBiddingEnabled" />
|
||||
<label class="form-check-label" for="probabilistic">
|
||||
<strong>Policy probabilistica</strong>
|
||||
<div class="form-text">Decide se puntare con probabilità basata su competizione</div>
|
||||
<div class="form-text">Decide se puntare con probabilità basata su competizione</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label">Probabilità base (0-1)</label>
|
||||
<label class="form-label">Probabilità base (0-1)</label>
|
||||
<input type="number" step="0.1" class="form-control" @bind="settings.BaseBidProbability" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -393,7 +393,7 @@
|
||||
<select class="form-select" @bind="settings.AggressiveBidderAction">
|
||||
<option value="Compete">? Continua normalmente (consigliato)</option>
|
||||
<option value="Avoid">?? Evita asta (pausa automatica)</option>
|
||||
<option value="Outbid">?? Punta più aggressivamente</option>
|
||||
<option value="Outbid">?? Punta più aggressivamente</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -419,11 +419,11 @@
|
||||
<input type="number" class="form-control" @bind="settings.MaxBidsPerAuction" />
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label class="form-label">Budget giornaliero (€)</label>
|
||||
<label class="form-label">Budget giornaliero (€)</label>
|
||||
<input type="number" step="0.01" class="form-control" @bind="settings.DailyBudgetEuro" />
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label class="form-label">Costo medio puntata (€)</label>
|
||||
<label class="form-label">Costo medio puntata (€)</label>
|
||||
<input type="number" step="0.01" class="form-control" @bind="settings.AverageBidCostEuro" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -680,6 +680,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ASPETTO E TEMA -->
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading-theme">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-theme" aria-expanded="false" aria-controls="collapse-theme">
|
||||
<i class="bi bi-moon-stars-fill me-2"></i> Aspetto e Tema
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse-theme" class="accordion-collapse collapse" aria-labelledby="heading-theme" data-bs-parent="#settingsAccordion">
|
||||
<div class="accordion-body">
|
||||
<p class="text-muted small mb-3">Personalizza l'aspetto visivo dell'applicazione. Le modifiche vengono applicate immediatamente.</p>
|
||||
|
||||
<div class="md-theme-toggle mb-3">
|
||||
<div class="md-theme-toggle-label">
|
||||
<strong><i class="bi bi-moon-fill me-2"></i>Modalità Scura</strong>
|
||||
<small>Sostituisce il tema chiaro con colori ottimizzati per ambienti con poca luce.</small>
|
||||
</div>
|
||||
<div class="form-check form-switch ms-3 mb-0" style="padding-left:3.5rem;">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="darkModeToggle"
|
||||
style="width:3rem;height:1.5rem;cursor:pointer;"
|
||||
checked="@settings.DarkMode"
|
||||
@onchange="ToggleDarkMode" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!string.IsNullOrEmpty(themeMessage))
|
||||
{
|
||||
<div class="alert alert-success border-0 mt-2 py-2">
|
||||
<i class="bi bi-check-circle me-2"></i>@themeMessage
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INFORMAZIONI APPLICAZIONE -->
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading-info">
|
||||
@@ -759,29 +793,14 @@
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.accordion-button {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.font-monospace {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 0.925rem;
|
||||
font-family: 'Roboto Mono', ui-monospace, monospace;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.3s ease-in;
|
||||
}
|
||||
|
||||
.fade-in { animation: fadeIn 0.25s ease-in; }
|
||||
@@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
from { opacity: 0; transform: translateY(-8px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -804,6 +823,9 @@ private bool applyToAllSuccess = false;
|
||||
private HashSet<string> applyingSettings = new();
|
||||
private string? singleSettingMessage = null;
|
||||
|
||||
// Tema
|
||||
private string? themeMessage = null;
|
||||
|
||||
private AutoBidder.Utilities.AppSettings settings = new();
|
||||
private System.Threading.Timer? updateTimer;
|
||||
|
||||
@@ -907,11 +929,11 @@ private System.Threading.Timer? updateTimer;
|
||||
break;
|
||||
case nameof(settings.DefaultMinPrice):
|
||||
auction.MinPrice = settings.DefaultMinPrice;
|
||||
settingLabel = $"Prezzo minimo (€{settings.DefaultMinPrice:F2})";
|
||||
settingLabel = $"Prezzo minimo (€{settings.DefaultMinPrice:F2})";
|
||||
break;
|
||||
case nameof(settings.DefaultMaxPrice):
|
||||
auction.MaxPrice = settings.DefaultMaxPrice;
|
||||
settingLabel = $"Prezzo massimo (€{settings.DefaultMaxPrice:F2})";
|
||||
settingLabel = $"Prezzo massimo (€{settings.DefaultMaxPrice:F2})";
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
@@ -1073,7 +1095,19 @@ private System.Threading.Timer? updateTimer;
|
||||
private void SaveSettings()
|
||||
{
|
||||
AutoBidder.Utilities.SettingsManager.Save(settings);
|
||||
_ = JSRuntime.InvokeVoidAsync("alert", "? Impostazioni salvate con successo!");
|
||||
_ = JSRuntime.InvokeVoidAsync("alert", "✅ Impostazioni salvate con successo!");
|
||||
}
|
||||
|
||||
private async Task ToggleDarkMode(ChangeEventArgs e)
|
||||
{
|
||||
settings.DarkMode = (bool)(e.Value ?? false);
|
||||
AutoBidder.Utilities.SettingsManager.Save(settings);
|
||||
await JSRuntime.InvokeVoidAsync("mdTheme.apply", settings.DarkMode);
|
||||
themeMessage = settings.DarkMode ? "Modalità scura attivata." : "Modalità chiara attivata.";
|
||||
StateHasChanged();
|
||||
await Task.Delay(2500);
|
||||
themeMessage = null;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void SetRememberState(bool remember)
|
||||
|
||||
+67
-404
@@ -20,7 +20,7 @@
|
||||
</div>
|
||||
@if (StatsService.IsAvailable)
|
||||
{
|
||||
<button class="action-btn primary" @onclick="RefreshStats" disabled="@isLoading" title="Aggiorna statistiche">
|
||||
<button class="md-icon-btn primary" @onclick="RefreshStats" disabled="@isLoading" title="Aggiorna statistiche">
|
||||
@if (isLoading)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm"></span>
|
||||
@@ -972,432 +972,95 @@ private bool isBulkOperating = false;
|
||||
}
|
||||
|
||||
<style>
|
||||
/* ??? PAGE LAYOUT ??? */
|
||||
.stats-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ??? TOOLBAR ??? */
|
||||
.stats-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.4rem 0.6rem;
|
||||
background: linear-gradient(135deg, rgba(20, 20, 30, 0.8) 0%, rgba(30, 30, 45, 0.8) 100%);
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.stats-toolbar-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.stats-toolbar-title i {
|
||||
font-size: 1.1rem;
|
||||
color: #818cf8;
|
||||
}
|
||||
|
||||
.action-btn.primary {
|
||||
background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
|
||||
color: white;
|
||||
}
|
||||
.action-btn.primary:hover {
|
||||
box-shadow: 0 0 10px rgba(99, 102, 241, 0.5);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* ??? ALERT / LOADING ??? */
|
||||
.stats-alert {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border: 1px solid rgba(245, 158, 11, 0.25);
|
||||
border-radius: var(--radius-md);
|
||||
color: #fbbf24;
|
||||
}
|
||||
.stats-alert i { font-size: 1.4rem; }
|
||||
.stats-alert strong { display: block; font-size: 0.8rem; }
|
||||
.stats-alert small { opacity: 0.7; font-size: 0.72rem; }
|
||||
|
||||
.stats-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 3rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Statistics page — MD3 overrides */
|
||||
.stats-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 2rem;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.78rem;
|
||||
gap: 8px;
|
||||
padding: 32px;
|
||||
color: var(--md-on-surface-variant);
|
||||
font-size: 13px;
|
||||
}
|
||||
.stats-empty i { font-size: 1.8rem; opacity: 0.4; }
|
||||
.stats-empty i { font-size: 2rem; opacity: 0.4; }
|
||||
|
||||
/* ??? PANELS ??? */
|
||||
.stats-panel-products {
|
||||
flex: 1 1 auto;
|
||||
min-height: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stats-panel-auctions {
|
||||
flex: 0 0 auto;
|
||||
max-height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ??? FILTER BAR (inside panel-header) ??? */
|
||||
.stats-filter-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
.stats-panel-products { flex: 1 1 auto; min-height: 200px; overflow: hidden; }
|
||||
.stats-panel-auctions { flex: 0 0 auto; max-height: 300px; overflow: hidden; }
|
||||
|
||||
/* Filter bar */
|
||||
.stats-filter-bar { display:flex; align-items:center; gap:6px; margin-left:auto; }
|
||||
.stats-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 0.15rem 0.4rem;
|
||||
font-size: 0.7rem;
|
||||
gap: 4px;
|
||||
background-color: var(--md-surface-container);
|
||||
border: 1px solid var(--md-outline-variant);
|
||||
border-radius: var(--md-shape-extra-small);
|
||||
padding: 4px 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.stats-search i { color: var(--text-secondary); font-size: 0.65rem; }
|
||||
.stats-search i { color: var(--md-on-surface-variant); font-size: 12px; }
|
||||
.stats-search input {
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.7rem;
|
||||
width: 120px;
|
||||
}
|
||||
.stats-search input::placeholder { color: rgba(255,255,255,0.25); }
|
||||
.stats-search-clear {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
font-size: 0.7rem;
|
||||
line-height: 1;
|
||||
color: var(--md-on-surface);
|
||||
font-size: 13px;
|
||||
width: 130px;
|
||||
font-family: var(--md-font-family);
|
||||
}
|
||||
.stats-search input::placeholder { color: var(--md-on-surface-variant); opacity: 0.6; }
|
||||
.stats-search-clear { background:none; border:none; color:var(--md-on-surface-variant); cursor:pointer; padding:0; font-size:14px; }
|
||||
|
||||
.stats-select {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.7rem;
|
||||
padding: 0.15rem 0.35rem;
|
||||
outline: none;
|
||||
}
|
||||
.stats-select option { background: var(--bg-card); }
|
||||
|
||||
/* ??? TABLE (shared) ??? */
|
||||
.stats-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.stats-table thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.stats-table th {
|
||||
padding: 0.4rem 0.5rem;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
color: var(--text-secondary);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.stats-table td {
|
||||
padding: 0.3rem 0.5rem;
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.stats-table tbody tr {
|
||||
transition: background 0.12s ease;
|
||||
}
|
||||
|
||||
.stats-table tbody tr:hover {
|
||||
background: rgba(99, 102, 241, 0.06);
|
||||
}
|
||||
|
||||
.stats-table tbody tr.row-won {
|
||||
background: rgba(34, 197, 94, 0.05);
|
||||
}
|
||||
.stats-table tbody tr.row-won:hover {
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
}
|
||||
|
||||
.sortable-header {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.sortable-header:hover {
|
||||
color: var(--text-primary);
|
||||
background: rgba(255, 255, 255, 0.06) !important;
|
||||
}
|
||||
|
||||
/* ??? CELL HELPERS ??? */
|
||||
.cell-name { font-weight: 600; }
|
||||
.cell-price { font-weight: 600; color: #818cf8; }
|
||||
.cell-muted { color: var(--text-secondary); font-size: 0.72rem; }
|
||||
|
||||
.stats-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 10px;
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
.badge-success { background: rgba(34, 197, 94, 0.15); color: #4ade80; }
|
||||
.badge-danger { background: rgba(239, 68, 68, 0.15); color: #f87171; }
|
||||
.badge-muted { background: rgba(255, 255, 255, 0.05); color: var(--text-secondary); }
|
||||
|
||||
.stats-price { font-weight: 600; color: #818cf8; }
|
||||
|
||||
/* ??? PRODUCT TABLE INLINE ??? */
|
||||
.product-table-inline {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.ps-col-name { width: auto; min-width: 180px; }
|
||||
.ps-col-num { width: 42px; }
|
||||
.ps-col-price { width: 62px; }
|
||||
.ps-col-toggle { width: 34px; }
|
||||
.ps-col-input { width: 72px; }
|
||||
.ps-col-actions { width: 120px; }
|
||||
|
||||
.ps-product-title {
|
||||
font-weight: 600;
|
||||
font-size: 0.78rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
.ps-price-stat {
|
||||
font-size: 0.72rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.ps-price-stat.ps-price-avg {
|
||||
font-weight: 600;
|
||||
color: #818cf8;
|
||||
}
|
||||
.ps-wins {
|
||||
color: #fbbf24;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ??? INLINE INPUT (DARK) ??? */
|
||||
/* Inline input (compact table cells) */
|
||||
.ps-inline-input {
|
||||
width: 100%;
|
||||
font-size: 0.7rem;
|
||||
padding: 0.15rem 0.3rem;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
padding: 3px 6px;
|
||||
height: 26px;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
color: var(--text-primary);
|
||||
border-radius: var(--md-shape-extra-small);
|
||||
border: 1px solid var(--md-outline);
|
||||
background-color: var(--md-surface-container-lowest);
|
||||
color: var(--md-on-surface);
|
||||
outline: none;
|
||||
transition: border-color 0.15s, box-shadow 0.15s;
|
||||
font-family: var(--md-font-family);
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.ps-inline-input:focus {
|
||||
border-color: rgba(99, 102, 241, 0.5);
|
||||
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
.ps-inline-input::placeholder {
|
||||
color: rgba(255, 255, 255, 0.2);
|
||||
font-style: italic;
|
||||
font-size: 0.62rem;
|
||||
}
|
||||
/* Hide number input spinners */
|
||||
.ps-inline-input:focus { border: 2px solid var(--md-primary); padding: 2px 5px; }
|
||||
.ps-inline-input::placeholder { color: var(--md-on-surface-variant); opacity: 0.5; font-style:italic; font-size:11px; }
|
||||
.ps-inline-input::-webkit-outer-spin-button,
|
||||
.ps-inline-input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
.ps-inline-input[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
/* ??? ACTION BUTTONS ??? */
|
||||
.ps-action-group {
|
||||
display: flex;
|
||||
gap: 3px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.product-row .btn-xs {
|
||||
padding: 0.12rem 0.3rem;
|
||||
font-size: 0.68rem;
|
||||
line-height: 1.2;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
.product-row .btn-xs i { font-size: 0.7rem; }
|
||||
|
||||
.btn-outline-info {
|
||||
background: transparent;
|
||||
border-color: rgba(56, 189, 248, 0.3);
|
||||
color: #38bdf8;
|
||||
}
|
||||
.btn-outline-info:hover { background: rgba(56, 189, 248, 0.15); }
|
||||
|
||||
.btn-success {
|
||||
background: rgba(34, 197, 94, 0.15);
|
||||
color: #4ade80;
|
||||
}
|
||||
.btn-success:hover { background: rgba(34, 197, 94, 0.3); }
|
||||
|
||||
.btn-primary {
|
||||
background: rgba(99, 102, 241, 0.15);
|
||||
color: #a5b4fc;
|
||||
}
|
||||
.btn-primary:hover { background: rgba(99, 102, 241, 0.3); }
|
||||
|
||||
.btn-outline-danger {
|
||||
background: transparent;
|
||||
border-color: rgba(239, 68, 68, 0.3);
|
||||
color: #f87171;
|
||||
}
|
||||
.btn-outline-danger:hover { background: rgba(239, 68, 68, 0.15); }
|
||||
|
||||
.btn-xs:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ??? PRODUCT ROW HOVER ??? */
|
||||
.product-row {
|
||||
transition: background 0.12s ease;
|
||||
}
|
||||
.product-row:hover {
|
||||
background: rgba(99, 102, 241, 0.04);
|
||||
}
|
||||
|
||||
/* ??? TOGGLE SWITCH ??? */
|
||||
.ps-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 28px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ps-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.ps-switch-slider {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
.ps-switch-slider::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background: var(--text-secondary);
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
.ps-switch input:checked + .ps-switch-slider {
|
||||
background: rgba(34, 197, 94, 0.3);
|
||||
}
|
||||
.ps-switch input:checked + .ps-switch-slider::before {
|
||||
transform: translateX(12px);
|
||||
background: #4ade80;
|
||||
}
|
||||
|
||||
/* ??? DISABLED INPUT ??? */
|
||||
.ps-inline-input::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }
|
||||
.ps-inline-input[type=number] { -moz-appearance:textfield; }
|
||||
.ps-inline-input.ps-input-disabled,
|
||||
.ps-inline-input:disabled {
|
||||
opacity: 0.25;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.ps-inline-input:disabled { opacity: 0.35; cursor: not-allowed; }
|
||||
|
||||
/* ═══ BULK ACTION BAR ═══ */
|
||||
.ps-bulk-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.ps-bulk-label {
|
||||
font-size: 0.62rem;
|
||||
color: var(--text-secondary);
|
||||
margin-right: 0.25rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ps-bulk-bar .btn-xs {
|
||||
padding: 0.12rem 0.4rem;
|
||||
font-size: 0.62rem;
|
||||
line-height: 1.3;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ps-bulk-bar .btn-xs i { font-size: 0.6rem; margin-right: 0.15rem; }
|
||||
.ps-bulk-bar .btn-xs:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.btn-outline-toggle {
|
||||
background: transparent;
|
||||
border-color: rgba(168, 162, 158, 0.25);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.btn-outline-toggle:hover { background: rgba(168, 162, 158, 0.1); }
|
||||
/* Column widths */
|
||||
.ps-col-select { width: 30px; }
|
||||
.ps-col-num { width: 44px; }
|
||||
.ps-col-price { width: 64px; }
|
||||
.ps-col-toggle { width: 36px; }
|
||||
.ps-col-input { width: 76px; }
|
||||
.ps-col-actions { width: 124px; }
|
||||
|
||||
.ps-product-title { font-weight:500; font-size:13px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:260px; color:var(--md-on-surface); }
|
||||
.ps-price-stat { font-size:12px; color:var(--md-on-surface-variant); }
|
||||
.ps-price-stat.ps-price-avg { font-weight:600; color:var(--md-primary); }
|
||||
.ps-wins { color:var(--md-warning); font-weight:600; }
|
||||
|
||||
/* Action groups */
|
||||
.ps-action-group { display:flex; gap:3px; justify-content:center; }
|
||||
|
||||
/* Toggle switch — MD3 tonal */
|
||||
.ps-switch { position:relative; display:inline-block; width:28px; height:16px; }
|
||||
.ps-switch input { opacity:0; width:0; height:0; }
|
||||
.ps-switch-slider { position:absolute; inset:0; background-color:var(--md-outline-variant); border-radius:999px; cursor:pointer; transition:.2s; }
|
||||
.ps-switch-slider::before { content:""; position:absolute; width:10px; height:10px; left:3px; bottom:3px; background:white; border-radius:50%; transition:.2s; }
|
||||
.ps-switch input:checked + .ps-switch-slider { background:var(--md-primary); }
|
||||
.ps-switch input:checked + .ps-switch-slider::before { transform:translateX(12px); }
|
||||
|
||||
/* Bulk action bar */
|
||||
.ps-bulk-bar { display:flex; align-items:center; gap:6px; padding:6px 12px; background-color:var(--md-surface-container-low); border-bottom:1px solid var(--md-outline-variant); flex-wrap:wrap; }
|
||||
.ps-bulk-label { font-size:12px; color:var(--md-on-surface-variant); margin-right:4px; white-space:nowrap; }
|
||||
</style>
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="~/" />
|
||||
<link rel="icon" type="image/x-icon" href="Icon/favicon.ico" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet" />
|
||||
<link href="css/material3.css" rel="stylesheet" />
|
||||
<link href="css/app.css" rel="stylesheet" />
|
||||
<link href="css/animations.css" rel="stylesheet" />
|
||||
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
|
||||
@@ -26,6 +30,17 @@
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
window.mdTheme = {
|
||||
apply: function (dark) {
|
||||
if (dark) {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
} else {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script src="_framework/blazor.server.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,103 +1,35 @@
|
||||
@inherits LayoutComponentBase
|
||||
@inherits LayoutComponentBase
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject Microsoft.AspNetCore.Components.NavigationManager NavigationManager
|
||||
|
||||
<div class="app-container">
|
||||
<aside class="app-sidebar">
|
||||
<NavMenu />
|
||||
</aside>
|
||||
<div class="md-app-container">
|
||||
<NavMenu />
|
||||
|
||||
<main class="app-main">
|
||||
<article class="app-content">
|
||||
<main class="md-app-main">
|
||||
<article class="md-app-content">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
<div class="error-content">
|
||||
<i class="bi bi-exclamation-triangle-fill"></i>
|
||||
<span>Si e verificato un errore. <a href="" class="reload">Ricarica</a></span>
|
||||
<button class="dismiss-btn" onclick="this.parentElement.parentElement.style.display='none'">×</button>
|
||||
</div>
|
||||
<i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<span>Si è verificato un errore. <a href="" class="reload">Ricarica</a></span>
|
||||
<a class="ms-3" onclick="document.getElementById('blazor-error-ui').style.display='none'" style="cursor:pointer;color:inherit;">✕</a>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.app-container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
background: #0f0f0f;
|
||||
}
|
||||
|
||||
.app-sidebar {
|
||||
width: 260px;
|
||||
min-width: 260px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.app-main {
|
||||
flex: 1;
|
||||
margin-left: 260px;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
flex: 1;
|
||||
padding: 1.5rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#blazor-error-ui {
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#blazor-error-ui .error-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.5rem;
|
||||
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#blazor-error-ui .reload {
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss-btn {
|
||||
margin-left: auto;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@media (max-width: 768px) {
|
||||
.app-sidebar {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app-main {
|
||||
margin-left: 0;
|
||||
@code {
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
try
|
||||
{
|
||||
var s = AutoBidder.Utilities.SettingsManager.Load();
|
||||
await JSRuntime.InvokeVoidAsync("mdTheme.apply", s.DarkMode);
|
||||
}
|
||||
catch { /* JS helper may not be available yet; theme defaults to light mode */ }
|
||||
}
|
||||
}
|
||||
</style>
|
||||
}
|
||||
|
||||
|
||||
+61
-250
@@ -1,91 +1,81 @@
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject AuctionMonitor AuctionMonitor
|
||||
@implements IDisposable
|
||||
|
||||
<div class="nav-sidebar">
|
||||
<div class="nav-header">
|
||||
<a class="nav-brand" href="">
|
||||
<div class="brand-icon">
|
||||
<i class="bi bi-lightning-charge-fill"></i>
|
||||
</div>
|
||||
<span class="brand-text">AutoBidder</span>
|
||||
<div class="md-nav-rail">
|
||||
|
||||
<!-- Brand icon -->
|
||||
<div class="md-rail-header">
|
||||
<a class="md-rail-brand-icon" href="" title="AutoBidder">
|
||||
<i class="bi bi-lightning-charge-fill"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<nav class="nav-menu">
|
||||
<div class="nav-section">
|
||||
<NavLink class="nav-menu-item" href="" Match="NavLinkMatch.All">
|
||||
<i class="bi bi-display"></i>
|
||||
<span>Monitor Aste</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink class="nav-menu-item" href="browser">
|
||||
<i class="bi bi-search"></i>
|
||||
<span>Esplora Aste</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink class="nav-menu-item" href="statistics">
|
||||
<i class="bi bi-bar-chart"></i>
|
||||
<span>Statistiche</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink class="nav-menu-item" href="settings">
|
||||
<i class="bi bi-gear"></i>
|
||||
<span>Impostazioni</span>
|
||||
</NavLink>
|
||||
</div>
|
||||
<!-- Main navigation destinations -->
|
||||
<nav class="md-rail-nav">
|
||||
<NavLink class="md-rail-item" href="" Match="NavLinkMatch.All" title="Monitor Aste">
|
||||
<div class="md-rail-indicator"><i class="bi bi-display"></i></div>
|
||||
<span class="md-rail-label">Monitor</span>
|
||||
</NavLink>
|
||||
|
||||
<div class="nav-footer">
|
||||
<!-- Info Sessione Utente -->
|
||||
@if (!string.IsNullOrEmpty(sessionUsername))
|
||||
{
|
||||
<div class="session-stats">
|
||||
<div class="session-stat">
|
||||
<i class="bi bi-hand-index-thumb-fill"></i>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">Puntate</span>
|
||||
<span class="stat-value @GetBidsClass()">@sessionRemainingBids</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="session-stat">
|
||||
<i class="bi bi-wallet2"></i>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">Credito</span>
|
||||
<span class="stat-value text-success">€@sessionShopCredit.ToString("F2")</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<div class="user-badge @(string.IsNullOrEmpty(sessionUsername) ? "disconnected" : "connected")">
|
||||
<i class="bi bi-person-circle"></i>
|
||||
<span>@(string.IsNullOrEmpty(sessionUsername) ? "Non connesso" : sessionUsername)</span>
|
||||
</div>
|
||||
<a href="/Account/Logout" class="nav-menu-item logout-item">
|
||||
<i class="bi bi-box-arrow-right"></i>
|
||||
<span>Logout</span>
|
||||
</a>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
</div>
|
||||
<NavLink class="md-rail-item" href="browser" title="Esplora Aste">
|
||||
<div class="md-rail-indicator"><i class="bi bi-search"></i></div>
|
||||
<span class="md-rail-label">Esplora</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink class="md-rail-item" href="statistics" title="Statistiche">
|
||||
<div class="md-rail-indicator"><i class="bi bi-bar-chart"></i></div>
|
||||
<span class="md-rail-label">Statistiche</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink class="md-rail-item" href="settings" title="Impostazioni">
|
||||
<div class="md-rail-indicator"><i class="bi bi-gear"></i></div>
|
||||
<span class="md-rail-label">Impostazioni</span>
|
||||
</NavLink>
|
||||
</nav>
|
||||
|
||||
<!-- Footer: session stats + user + logout -->
|
||||
<div class="md-rail-footer">
|
||||
|
||||
@if (!string.IsNullOrEmpty(sessionUsername))
|
||||
{
|
||||
<div class="md-rail-stat @GetBidsClass()" title="Puntate rimanenti: @sessionRemainingBids">
|
||||
<i class="bi bi-hand-index-thumb-fill"></i>
|
||||
<span>@sessionRemainingBids</span>
|
||||
</div>
|
||||
<div class="md-rail-stat" style="color:var(--md-success);" title="Credito: €@sessionShopCredit.ToString("F2")">
|
||||
<i class="bi bi-wallet2"></i>
|
||||
<span style="font-size:10px;">€@sessionShopCredit.ToString("F0")</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<div class="md-rail-user @(string.IsNullOrEmpty(sessionUsername) ? "disconnected" : "connected")"
|
||||
title="@(string.IsNullOrEmpty(sessionUsername) ? "Non connesso" : sessionUsername)">
|
||||
<i class="bi bi-person-circle"></i>
|
||||
</div>
|
||||
<a href="/Account/Logout" class="md-rail-logout" title="Logout">
|
||||
<div class="md-rail-indicator"><i class="bi bi-box-arrow-right"></i></div>
|
||||
<span class="md-rail-label">Esci</span>
|
||||
</a>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string? sessionUsername;
|
||||
private int sessionRemainingBids;
|
||||
private double sessionShopCredit;
|
||||
private System.Threading.Timer? refreshTimer;
|
||||
private string? sessionUsername;
|
||||
private int sessionRemainingBids;
|
||||
private double sessionShopCredit;
|
||||
private System.Threading.Timer? refreshTimer;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
LoadSessionInfo();
|
||||
|
||||
// Refresh ogni 5 secondi
|
||||
|
||||
refreshTimer = new System.Threading.Timer(async _ =>
|
||||
{
|
||||
LoadSessionInfo();
|
||||
@@ -120,182 +110,3 @@ private System.Threading.Timer? refreshTimer;
|
||||
refreshTimer?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
<style>
|
||||
.nav-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, #1a1d23 0%, #13151a 100%);
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.nav-header {
|
||||
padding: 1.25rem 1.5rem;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.nav-brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.nav-brand:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.brand-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||||
border-radius: 10px;
|
||||
font-size: 1.25rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 1rem 0.75rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.nav-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.nav-menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 8px;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
text-decoration: none;
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.nav-menu-item i {
|
||||
font-size: 1.125rem;
|
||||
width: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-menu-item:hover {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-menu-item.active {
|
||||
background: linear-gradient(135deg, rgba(99, 102, 241, 0.15) 0%, rgba(139, 92, 246, 0.15) 100%);
|
||||
color: #a5b4fc;
|
||||
}
|
||||
|
||||
.nav-menu-item.active i {
|
||||
color: #818cf8;
|
||||
}
|
||||
|
||||
.nav-footer {
|
||||
margin-top: auto;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
|
||||
.session-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.session-stat {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
padding: 0.375rem 0;
|
||||
}
|
||||
|
||||
.session-stat i {
|
||||
font-size: 0.875rem;
|
||||
width: 1.25rem;
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.session-stat .stat-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.session-stat .stat-label {
|
||||
font-size: 0.75rem;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.session-stat .stat-value {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.session-stat .text-success { color: #22c55e; }
|
||||
.session-stat .text-warning { color: #f59e0b; }
|
||||
.session-stat .text-danger { color: #ef4444; }
|
||||
|
||||
.user-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 8px;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.user-badge.connected {
|
||||
border-left: 3px solid #22c55e;
|
||||
}
|
||||
|
||||
.user-badge.disconnected {
|
||||
border-left: 3px solid #ef4444;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.user-badge i {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.logout-item {
|
||||
color: rgba(248, 113, 113, 0.8) !important;
|
||||
}
|
||||
|
||||
.logout-item:hover {
|
||||
background: rgba(248, 113, 113, 0.1) !important;
|
||||
color: #f87171 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -429,6 +429,16 @@ namespace AutoBidder.Utilities
|
||||
/// Default: true
|
||||
/// </summary>
|
||||
public bool SaveBidMetricsToDatabase { get; set; } = true;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// INTERFACCIA
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>
|
||||
/// Abilita la modalità scura dell'interfaccia.
|
||||
/// Default: false (modalità chiara)
|
||||
/// </summary>
|
||||
public bool DarkMode { get; set; } = false;
|
||||
}
|
||||
|
||||
public static class SettingsManager
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user