littleshop/LittleShop/Areas/Admin/Views/Dashboard/Index.cshtml
SysAdmin 1aed286fac
All checks were successful
Build and Deploy LittleShop / Deploy to Production VPS (Manual Only) (push) Has been skipped
Build and Deploy LittleShop / Deploy to Pre-Production (CT109) (push) Successful in 1m0s
feat: Display runtime connection string in dashboard
Added connection string display to System Information section of dashboard.

- Injected IConfiguration into DashboardController
- Added ConnectionString to ViewData
- Displayed in monospace code format for easy reading
- Shows actual runtime connection string from configuration
- Helps verify which database file is being used in different environments

This makes it easier to troubleshoot database location issues, especially
when deploying to different environments (Development, Production, CT109, etc.).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 20:24:53 +00:00

289 lines
12 KiB
Plaintext

@{
ViewData["Title"] = "Dashboard";
}
<div class="row mb-3">
<div class="col">
<h1><i class="fas fa-tachometer-alt"></i> Dashboard</h1>
<p class="text-muted mb-0">Welcome back! Here's what needs your attention today.</p>
</div>
</div>
<!-- PWA Install Prompt (Inline Content) -->
<div id="pwa-install-alert" class="alert alert-info alert-dismissible fade show d-flex align-items-center" role="alert" style="display: none !important;">
<i class="fas fa-mobile-alt me-3 fs-4"></i>
<div class="flex-grow-1">
<strong>Install TeleShop Admin as an App</strong>
<p class="mb-0 small">Get a better experience with offline access and a native app feel.</p>
</div>
<button id="pwa-install-btn" class="btn btn-primary btn-sm me-2">
<i class="fas fa-download"></i> Install
</button>
<button id="pwa-dismiss-btn" type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@{
var pendingOrders = (int)ViewData["PendingOrders"]!;
var lowStockProducts = (int)ViewData["LowStockProducts"]!;
var outOfStockProducts = (int)ViewData["OutOfStockProducts"]!;
var totalUrgentActions = pendingOrders + (lowStockProducts > 5 ? 1 : 0) + (outOfStockProducts > 0 ? 1 : 0);
}
<!-- URGENT ACTIONS PANEL -->
@if (totalUrgentActions > 0)
{
<div class="row mb-4">
<div class="col">
<div class="card border-warning shadow-sm">
<div class="card-header bg-warning text-dark">
<h5 class="mb-0">
<i class="fas fa-exclamation-triangle"></i>
<strong>Urgent Actions Required</strong>
<span class="badge bg-danger ms-2">@totalUrgentActions</span>
</h5>
</div>
<div class="card-body">
<div class="list-group list-group-flush">
@if (pendingOrders > 0)
{
<a href="@Url.Action("Index", "Orders", new { area = "Admin", status = "PendingPayment" })"
class="list-group-item list-group-item-action list-group-item-warning d-flex justify-content-between align-items-center">
<div>
<i class="fas fa-shopping-cart text-warning me-2"></i>
<strong>@pendingOrders order@(pendingOrders != 1 ? "s" : "") awaiting payment</strong>
<small class="d-block text-muted">Customers may need payment reminders</small>
</div>
<i class="fas fa-chevron-right"></i>
</a>
}
@if (outOfStockProducts > 0)
{
<a href="@Url.Action("Index", "Products", new { area = "Admin" })"
class="list-group-item list-group-item-action list-group-item-danger d-flex justify-content-between align-items-center">
<div>
<i class="fas fa-box-open text-danger me-2"></i>
<strong>@outOfStockProducts product@(outOfStockProducts != 1 ? "s" : "") out of stock</strong>
<small class="d-block text-muted">Update inventory or mark as unavailable</small>
</div>
<i class="fas fa-chevron-right"></i>
</a>
}
@if (lowStockProducts > 5)
{
<a href="@Url.Action("Index", "Products", new { area = "Admin" })"
class="list-group-item list-group-item-action list-group-item-warning d-flex justify-content-between align-items-center">
<div>
<i class="fas fa-exclamation-circle text-warning me-2"></i>
<strong>@lowStockProducts products running low on stock</strong>
<small class="d-block text-muted">Stock levels below 10 units</small>
</div>
<i class="fas fa-chevron-right"></i>
</a>
}
</div>
</div>
</div>
</div>
</div>
}
else
{
<div class="row mb-4">
<div class="col">
<div class="alert alert-success d-flex align-items-center" role="alert">
<i class="fas fa-check-circle fs-4 me-3"></i>
<div>
<strong>All systems running smoothly!</strong>
<p class="mb-0 small">No urgent actions required at this time.</p>
</div>
</div>
</div>
</div>
}
<div class="row">
<div class="col-md-3">
<div class="card text-white bg-primary mb-3">
<div class="card-header">
<i class="fas fa-shopping-cart"></i> Total Orders
</div>
<div class="card-body">
<h4 class="card-title">@ViewData["TotalOrders"]</h4>
<small>@ViewData["PendingOrders"] pending • @ViewData["ShippedOrders"] shipped</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-success mb-3">
<div class="card-header">
<i class="fas fa-box"></i> Total Products
</div>
<div class="card-body">
<h4 class="card-title">@ViewData["TotalProducts"]</h4>
<small>@ViewData["TotalVariations"] variations • @ViewData["TotalStock"] in stock</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-info mb-3">
<div class="card-header">
<i class="fas fa-tags"></i> Categories
</div>
<div class="card-body">
<h4 class="card-title">@ViewData["TotalCategories"]</h4>
<small>Active categories</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-warning mb-3">
<div class="card-header">
<i class="fas fa-pound-sign"></i> Total Revenue
</div>
<div class="card-body">
<h4 class="card-title">£@ViewData["TotalRevenue"]</h4>
<small>From completed orders</small>
</div>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-md-3">
<div class="card text-white bg-danger mb-3">
<div class="card-header">
<i class="fas fa-exclamation-triangle"></i> Stock Alerts
</div>
<div class="card-body">
<h4 class="card-title">@ViewData["LowStockProducts"]</h4>
<small>@ViewData["OutOfStockProducts"] out of stock</small>
</div>
</div>
</div>
<div class="col-md-9">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-list"></i> Product Variations Summary</h5>
</div>
<div class="card-body">
@if (ViewData["TotalVariants"] != null && (int)ViewData["TotalVariants"] > 0)
{
<div class="alert alert-success">
<i class="fas fa-check-circle"></i>
<strong>@ViewData["TotalVariants"] product variations</strong> have been configured across your catalog.
Customers can now choose quantity-based pricing options!
</div>
}
else
{
<div class="alert alert-info">
<i class="fas fa-info-circle"></i>
No product variations configured yet.
<a href="@Url.Action("Index", "Products")" class="alert-link">Add variations</a>
to offer quantity-based pricing (e.g., 1 for £10, 2 for £19, 3 for £25).
</div>
}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-chart-line"></i> Quick Actions</h5>
</div>
<div class="card-body">
<div class="list-group list-group-flush">
<a href="@Url.Action("Create", "Products")" class="list-group-item list-group-item-action">
<i class="fas fa-plus"></i> Add New Product
</a>
<a href="@Url.Action("Create", "Categories")" class="list-group-item list-group-item-action">
<i class="fas fa-plus"></i> Add New Category
</a>
<a href="@Url.Action("Index", "Orders")" class="list-group-item list-group-item-action">
<i class="fas fa-list"></i> View All Orders
</a>
<a href="@Url.Action("Create", "Users")" class="list-group-item list-group-item-action">
<i class="fas fa-user-plus"></i> Add New User
</a>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5><i class="fas fa-info-circle"></i> System Information</h5>
</div>
<div class="card-body">
<ul class="list-unstyled">
<li><strong>Framework:</strong> .NET 9.0</li>
<li><strong>Database:</strong> SQLite</li>
<li><strong>Connection String:</strong> <code class="text-muted small">@ViewData["ConnectionString"]</code></li>
<li><strong>Authentication:</strong> Cookie-based</li>
<li><strong>Crypto Support:</strong> 8 currencies via BTCPay Server</li>
<li><strong>API Endpoints:</strong> Available for client integration</li>
</ul>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const installAlert = document.getElementById('pwa-install-alert');
const installBtn = document.getElementById('pwa-install-btn');
const dismissBtn = document.getElementById('pwa-dismiss-btn');
// Check if user has dismissed the alert
const alertDismissed = localStorage.getItem('pwa-install-dismissed');
// Check if app is in standalone mode (already installed)
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
// Show alert if not dismissed and not already installed
if (installAlert && !alertDismissed && !isStandalone) {
installAlert.style.display = 'flex';
}
if (installBtn) {
installBtn.addEventListener('click', function() {
// Check if app is in standalone mode
if (isStandalone) {
alert('App is already installed!');
return;
}
// Show manual install instructions
alert(`To install TeleShop Admin as an app:
🌐 Chrome/Edge:
1. Click the install icon (⊞) in the address bar, OR
2. Menu (⋮) → "Install TeleShop Admin"
🍎 Safari (iOS):
1. Share button → "Add to Home Screen"
📱 Mobile browsers:
1. Browser menu → "Add to Home Screen"
The app will then work offline and appear in your apps list!`);
});
}
if (dismissBtn) {
dismissBtn.addEventListener('click', function() {
localStorage.setItem('pwa-install-dismissed', 'true');
});
}
});
</script>