PWA-implementation
@ -59,6 +59,9 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h5><i class="fas fa-chart-line"></i> Quick Actions</h5>
|
<h5><i class="fas fa-chart-line"></i> Quick Actions</h5>
|
||||||
|
<button id="pwa-install-dashboard" class="btn btn-sm btn-outline-primary" style="float: right;">
|
||||||
|
<i class="fas fa-mobile-alt"></i> Install App
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="list-group list-group-flush">
|
<div class="list-group list-group-flush">
|
||||||
@ -95,4 +98,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const installBtn = document.getElementById('pwa-install-dashboard');
|
||||||
|
if (installBtn) {
|
||||||
|
installBtn.addEventListener('click', function() {
|
||||||
|
// Check if app is in standalone mode
|
||||||
|
if (window.matchMedia('(display-mode: standalone)').matches) {
|
||||||
|
alert('App is already installed!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show manual install instructions
|
||||||
|
alert(`To install LittleShop Admin as an app:
|
||||||
|
|
||||||
|
🌐 Chrome/Edge:
|
||||||
|
1. Click the install icon (⊞) in the address bar, OR
|
||||||
|
2. Menu (⋮) → "Install LittleShop 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!`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@ -2,15 +2,42 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||||
<title>@ViewData["Title"] - LittleShop Admin</title>
|
<title>@ViewData["Title"] - LittleShop Admin</title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
<!-- PWA Meta Tags -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
<meta name="application-name" content="LittleShop Admin" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||||
|
<meta name="apple-mobile-web-app-title" content="LittleShop" />
|
||||||
|
<meta name="description" content="Modern e-commerce admin panel" />
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="theme-color" content="#2563eb" />
|
||||||
|
<meta name="msapplication-TileColor" content="#2563eb" />
|
||||||
|
<meta name="msapplication-tap-highlight" content="no" />
|
||||||
|
|
||||||
|
<!-- PWA Manifest -->
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
|
||||||
|
<!-- Icons -->
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" href="/icons/icon-152x152.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/icons/icon-72x72.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="96x96" href="/icons/icon-96x96.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="128x128" href="/icons/icon-128x128.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/icons/icon-144x144.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/icons/icon-152x152.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="192x192" href="/icons/icon-192x192.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="384x384" href="/icons/icon-384x384.png" />
|
||||||
|
<link rel="apple-touch-icon" sizes="512x512" href="/icons/icon-512x512.png" />
|
||||||
|
<link href="/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="/lib/fontawesome/css/all.min.css" rel="stylesheet">
|
||||||
|
<link href="/lib/bootstrap-icons/bootstrap-icons.css" rel="stylesheet">
|
||||||
|
<link href="/css/modern-admin.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
|
<nav class="navbar navbar-expand-sm navbar-light bg-white">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="@Url.Action("Index", "Dashboard", new { area = "Admin" })">
|
<a class="navbar-brand" href="@Url.Action("Index", "Dashboard", new { area = "Admin" })">
|
||||||
<i class="fas fa-store"></i> LittleShop Admin
|
<i class="fas fa-store"></i> LittleShop Admin
|
||||||
@ -87,8 +114,10 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="/lib/jquery/jquery.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="/js/pwa.js"></script>
|
||||||
|
<script src="/js/modern-mobile.js"></script>
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -7,5 +7,8 @@ public enum PaymentStatus
|
|||||||
Paid = 2,
|
Paid = 2,
|
||||||
Overpaid = 3,
|
Overpaid = 3,
|
||||||
Expired = 4,
|
Expired = 4,
|
||||||
Cancelled = 5
|
Cancelled = 5,
|
||||||
|
Processing = 6,
|
||||||
|
Completed = 7,
|
||||||
|
Failed = 8
|
||||||
}
|
}
|
||||||
@ -83,9 +83,28 @@ public class BTCPayServerService : IBTCPayServerService
|
|||||||
|
|
||||||
public Task<bool> ValidateWebhookAsync(string payload, string signature)
|
public Task<bool> ValidateWebhookAsync(string payload, string signature)
|
||||||
{
|
{
|
||||||
// Implement webhook signature validation
|
try
|
||||||
// This is a simplified version - in production, implement proper HMAC validation
|
{
|
||||||
return Task.FromResult(true);
|
// BTCPay Server uses HMAC-SHA256 with format "sha256=<hex>"
|
||||||
|
if (!signature.StartsWith("sha256="))
|
||||||
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedHash = signature.Substring(7); // Remove "sha256=" prefix
|
||||||
|
var secretBytes = System.Text.Encoding.UTF8.GetBytes(_webhookSecret);
|
||||||
|
var payloadBytes = System.Text.Encoding.UTF8.GetBytes(payload);
|
||||||
|
|
||||||
|
using var hmac = new System.Security.Cryptography.HMACSHA256(secretBytes);
|
||||||
|
var computedHash = hmac.ComputeHash(payloadBytes);
|
||||||
|
var computedHashHex = Convert.ToHexString(computedHash).ToLowerInvariant();
|
||||||
|
|
||||||
|
return Task.FromResult(expectedHash.Equals(computedHashHex, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetCurrencyCode(CryptoCurrency currency)
|
private static string GetCurrencyCode(CryptoCurrency currency)
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
"ExpiryInHours": 24
|
"ExpiryInHours": 24
|
||||||
},
|
},
|
||||||
"BTCPayServer": {
|
"BTCPayServer": {
|
||||||
"BaseUrl": "https://your-btcpay-server.com",
|
"BaseUrl": "https://pay.silverlabs.uk",
|
||||||
"ApiKey": "your-api-key",
|
"ApiKey": "your-api-key",
|
||||||
"StoreId": "your-store-id",
|
"StoreId": "your-store-id",
|
||||||
"WebhookSecret": "your-webhook-secret"
|
"WebhookSecret": "your-webhook-secret"
|
||||||
|
|||||||
415
LittleShop/wwwroot/css/corporate-steel-theme.css
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
/*
|
||||||
|
* 🏢 CORPORATE STEEL THEME 🏢
|
||||||
|
* Professional dark theme with subtle metallic accents
|
||||||
|
* Clean, corporate aesthetic with steel/metal textures
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Corporate Color Palette */
|
||||||
|
--corp-primary: #6A4C93; /* Muted Purple */
|
||||||
|
--corp-secondary: #8B5A8C; /* Soft Purple */
|
||||||
|
--corp-accent: #9B69B0; /* Light Purple */
|
||||||
|
|
||||||
|
/* Steel/Metal Colors */
|
||||||
|
--steel-dark: #1C1C1E; /* Dark Steel */
|
||||||
|
--steel-medium: #2C2C2E; /* Medium Steel */
|
||||||
|
--steel-light: #3A3A3C; /* Light Steel */
|
||||||
|
--steel-accent: #48484A; /* Steel Accent */
|
||||||
|
|
||||||
|
/* Text Colors */
|
||||||
|
--text-primary: #FFFFFF; /* White */
|
||||||
|
--text-secondary: #E5E5E7; /* Light Grey */
|
||||||
|
--text-muted: #AEAEB2; /* Muted Grey */
|
||||||
|
|
||||||
|
/* Subtle Gradients */
|
||||||
|
--steel-gradient: linear-gradient(135deg,
|
||||||
|
var(--steel-dark) 0%,
|
||||||
|
var(--steel-medium) 50%,
|
||||||
|
var(--steel-light) 100%);
|
||||||
|
|
||||||
|
--purple-gradient: linear-gradient(135deg,
|
||||||
|
var(--corp-primary) 0%,
|
||||||
|
var(--corp-secondary) 100%);
|
||||||
|
|
||||||
|
/* Shadows */
|
||||||
|
--shadow-subtle: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||||
|
--shadow-medium: 0 4px 16px rgba(0, 0, 0, 0.4);
|
||||||
|
--shadow-strong: 0 8px 32px rgba(0, 0, 0, 0.5);
|
||||||
|
|
||||||
|
/* Transitions */
|
||||||
|
--transition-smooth: 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global Corporate Base */
|
||||||
|
body {
|
||||||
|
background: var(--steel-dark) !important;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(45deg, transparent 49%, rgba(106, 76, 147, 0.03) 50%, transparent 51%),
|
||||||
|
linear-gradient(-45deg, transparent 49%, rgba(139, 90, 140, 0.02) 50%, transparent 51%);
|
||||||
|
background-size: 20px 20px, 24px 24px;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
font-family: 'Segoe UI', 'Roboto', 'Arial', sans-serif !important;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Styling */
|
||||||
|
.card,
|
||||||
|
.rz-card {
|
||||||
|
background: var(--steel-gradient) !important;
|
||||||
|
border: 1px solid var(--steel-accent) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
box-shadow: var(--shadow-medium) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
transition: all var(--transition-smooth) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover,
|
||||||
|
.rz-card:hover {
|
||||||
|
transform: translateY(-2px) !important;
|
||||||
|
box-shadow: var(--shadow-strong) !important;
|
||||||
|
border-color: var(--corp-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header,
|
||||||
|
.rz-card-header {
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
var(--steel-medium) 0%,
|
||||||
|
var(--steel-light) 100%) !important;
|
||||||
|
border-bottom: 1px solid var(--steel-accent) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body,
|
||||||
|
.rz-card-body {
|
||||||
|
background: var(--steel-medium) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button Styling */
|
||||||
|
.btn,
|
||||||
|
.rz-button {
|
||||||
|
background: var(--purple-gradient) !important;
|
||||||
|
border: 1px solid var(--corp-accent) !important;
|
||||||
|
border-radius: 6px !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
transition: all var(--transition-smooth) !important;
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover,
|
||||||
|
.rz-button:hover {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
var(--corp-secondary) 0%,
|
||||||
|
var(--corp-accent) 100%) !important;
|
||||||
|
border-color: var(--corp-accent) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:active,
|
||||||
|
.rz-button:active {
|
||||||
|
transform: scale(0.98) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navigation */
|
||||||
|
.navbar-dark {
|
||||||
|
background: var(--steel-gradient) !important;
|
||||||
|
border-bottom: 1px solid var(--steel-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
color: var(--text-secondary) !important;
|
||||||
|
transition: all var(--transition-smooth) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link:hover {
|
||||||
|
color: var(--corp-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form Controls */
|
||||||
|
.form-control,
|
||||||
|
.form-select,
|
||||||
|
.rz-textbox,
|
||||||
|
.rz-dropdown {
|
||||||
|
background: var(--steel-medium) !important;
|
||||||
|
border: 2px solid var(--steel-accent) !important;
|
||||||
|
border-radius: 6px !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
transition: all var(--transition-smooth) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus,
|
||||||
|
.form-select:focus,
|
||||||
|
.rz-textbox:focus,
|
||||||
|
.rz-dropdown:focus {
|
||||||
|
background: var(--steel-light) !important;
|
||||||
|
border-color: var(--corp-primary) !important;
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(106, 76, 147, 0.25) !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
color: var(--text-secondary) !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
.table {
|
||||||
|
background: var(--steel-medium) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
background: var(--steel-light) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
border-bottom: 2px solid var(--steel-accent) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td {
|
||||||
|
border-bottom: 1px solid var(--steel-accent) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr:hover {
|
||||||
|
background: rgba(106, 76, 147, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Navigation */
|
||||||
|
.mobile-header {
|
||||||
|
background: var(--steel-gradient) !important;
|
||||||
|
border-bottom: 1px solid var(--steel-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-bottom-nav {
|
||||||
|
background: var(--steel-medium) !important;
|
||||||
|
border-top: 1px solid var(--steel-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-nav-item {
|
||||||
|
color: var(--text-secondary) !important;
|
||||||
|
transition: all var(--transition-smooth) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-nav-item:hover {
|
||||||
|
color: var(--corp-accent) !important;
|
||||||
|
background: rgba(106, 76, 147, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-nav-item.active {
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
background: var(--purple-gradient) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-sidebar {
|
||||||
|
background: var(--steel-gradient) !important;
|
||||||
|
border-right: 1px solid var(--steel-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-sidebar-header {
|
||||||
|
background: var(--purple-gradient) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-sidebar-link {
|
||||||
|
color: var(--text-secondary) !important;
|
||||||
|
transition: all var(--transition-smooth) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-sidebar-link:hover {
|
||||||
|
background: rgba(106, 76, 147, 0.1) !important;
|
||||||
|
color: var(--corp-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-sidebar-link.active {
|
||||||
|
background: var(--purple-gradient) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alerts and Notifications */
|
||||||
|
.alert {
|
||||||
|
background: var(--steel-medium) !important;
|
||||||
|
border: 1px solid var(--steel-accent) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
border-color: #28a745 !important;
|
||||||
|
background: linear-gradient(135deg, var(--steel-medium), rgba(40, 167, 69, 0.1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
border-color: #dc3545 !important;
|
||||||
|
background: linear-gradient(135deg, var(--steel-medium), rgba(220, 53, 69, 0.1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-warning {
|
||||||
|
border-color: #ffc107 !important;
|
||||||
|
background: linear-gradient(135deg, var(--steel-medium), rgba(255, 193, 7, 0.1)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Cards */
|
||||||
|
.mobile-card,
|
||||||
|
.order-card {
|
||||||
|
background: var(--steel-gradient) !important;
|
||||||
|
border: 1px solid var(--steel-accent) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-card-header,
|
||||||
|
.order-header {
|
||||||
|
background: var(--steel-light) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
border-bottom: 1px solid var(--steel-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-card-body,
|
||||||
|
.order-body {
|
||||||
|
background: var(--steel-medium) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-card-footer {
|
||||||
|
background: var(--steel-light) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
border-top: 1px solid var(--steel-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status Badges */
|
||||||
|
.status-badge {
|
||||||
|
background: var(--steel-light) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
border: 1px solid var(--steel-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.pending {
|
||||||
|
background: linear-gradient(135deg, var(--steel-light), rgba(255, 193, 7, 0.2)) !important;
|
||||||
|
color: #ffc107 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.processing {
|
||||||
|
background: linear-gradient(135deg, var(--steel-light), rgba(23, 162, 184, 0.2)) !important;
|
||||||
|
color: #17a2b8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.shipped {
|
||||||
|
background: linear-gradient(135deg, var(--steel-light), rgba(40, 167, 69, 0.2)) !important;
|
||||||
|
color: #28a745 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Breadcrumbs and Links */
|
||||||
|
a {
|
||||||
|
color: var(--corp-accent) !important;
|
||||||
|
transition: color var(--transition-smooth) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdowns */
|
||||||
|
.dropdown-menu {
|
||||||
|
background: var(--steel-medium) !important;
|
||||||
|
border: 1px solid var(--steel-accent) !important;
|
||||||
|
box-shadow: var(--shadow-medium) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
color: var(--text-secondary) !important;
|
||||||
|
transition: all var(--transition-smooth) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item:hover {
|
||||||
|
background: rgba(106, 76, 147, 0.2) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List Groups */
|
||||||
|
.list-group-item {
|
||||||
|
background: var(--steel-medium) !important;
|
||||||
|
border: 1px solid var(--steel-accent) !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item:hover {
|
||||||
|
background: var(--steel-light) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override any remaining Bootstrap defaults */
|
||||||
|
.container,
|
||||||
|
.container-fluid {
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
color: var(--text-primary) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--text-secondary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle Steel Texture */
|
||||||
|
.steel-texture {
|
||||||
|
background-image:
|
||||||
|
linear-gradient(45deg, rgba(255,255,255,0.02) 25%, transparent 25%),
|
||||||
|
linear-gradient(-45deg, rgba(255,255,255,0.02) 25%, transparent 25%),
|
||||||
|
linear-gradient(45deg, transparent 75%, rgba(255,255,255,0.02) 75%),
|
||||||
|
linear-gradient(-45deg, transparent 75%, rgba(255,255,255,0.02) 75%);
|
||||||
|
background-size: 4px 4px;
|
||||||
|
background-position: 0 0, 0 2px, 2px -2px, -2px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Corporate Professional Styling */
|
||||||
|
.corporate-card {
|
||||||
|
background: var(--steel-gradient) !important;
|
||||||
|
border: 1px solid var(--steel-accent) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
box-shadow: var(--shadow-subtle) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.corporate-card:hover {
|
||||||
|
box-shadow: var(--shadow-medium) !important;
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove any epileptic-inducing effects */
|
||||||
|
* {
|
||||||
|
animation: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean scrollbars */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
background: var(--steel-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--steel-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--steel-light);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--corp-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Professional Focus States */
|
||||||
|
.form-control:focus,
|
||||||
|
.form-select:focus {
|
||||||
|
border-color: var(--corp-primary) !important;
|
||||||
|
box-shadow: 0 0 0 0.2rem rgba(106, 76, 147, 0.25) !important;
|
||||||
|
}
|
||||||
442
LittleShop/wwwroot/css/modern-admin.css
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
/*
|
||||||
|
* Modern Clean Admin Theme
|
||||||
|
* Mobile-first, professional, and user-friendly
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Modern Color Palette */
|
||||||
|
--primary-blue: #2563eb;
|
||||||
|
--primary-purple: #7c3aed;
|
||||||
|
--success-green: #059669;
|
||||||
|
--warning-orange: #d97706;
|
||||||
|
--danger-red: #dc2626;
|
||||||
|
|
||||||
|
/* Neutral Greys */
|
||||||
|
--grey-50: #f9fafb;
|
||||||
|
--grey-100: #f3f4f6;
|
||||||
|
--grey-200: #e5e7eb;
|
||||||
|
--grey-300: #d1d5db;
|
||||||
|
--grey-400: #9ca3af;
|
||||||
|
--grey-500: #6b7280;
|
||||||
|
--grey-600: #4b5563;
|
||||||
|
--grey-700: #374151;
|
||||||
|
--grey-800: #1f2937;
|
||||||
|
--grey-900: #111827;
|
||||||
|
|
||||||
|
/* Spacing */
|
||||||
|
--spacing-xs: 0.25rem;
|
||||||
|
--spacing-sm: 0.5rem;
|
||||||
|
--spacing-md: 1rem;
|
||||||
|
--spacing-lg: 1.5rem;
|
||||||
|
--spacing-xl: 2rem;
|
||||||
|
|
||||||
|
/* Border Radius */
|
||||||
|
--radius-sm: 0.375rem;
|
||||||
|
--radius-md: 0.5rem;
|
||||||
|
--radius-lg: 0.75rem;
|
||||||
|
--radius-xl: 1rem;
|
||||||
|
|
||||||
|
/* Shadows */
|
||||||
|
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||||
|
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
--font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global Styles */
|
||||||
|
body {
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
background-color: var(--grey-50);
|
||||||
|
color: var(--grey-900);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern Card Styling */
|
||||||
|
.card {
|
||||||
|
border: 1px solid var(--grey-200);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
background: white;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover {
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background: var(--grey-50);
|
||||||
|
border-bottom: 1px solid var(--grey-200);
|
||||||
|
padding: var(--spacing-lg);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--grey-700);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
padding: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern Button Styling */
|
||||||
|
.btn {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.625rem 1.25rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-purple) 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: linear-gradient(135deg, #1d4ed8 0%, #6d28d9 100%);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background: var(--success-green);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success:hover {
|
||||||
|
background: #047857;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary {
|
||||||
|
border: 2px solid var(--primary-blue);
|
||||||
|
color: var(--primary-blue);
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline-primary:hover {
|
||||||
|
background: var(--primary-blue);
|
||||||
|
color: white;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern Navigation */
|
||||||
|
.navbar {
|
||||||
|
background: white !important;
|
||||||
|
border-bottom: 1px solid var(--grey-200);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
padding: var(--spacing-md) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-brand {
|
||||||
|
color: var(--grey-900) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav .nav-link {
|
||||||
|
color: var(--grey-600) !important;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
margin: 0 0.25rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav .nav-link:hover {
|
||||||
|
color: var(--primary-blue) !important;
|
||||||
|
background: var(--grey-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form Controls */
|
||||||
|
.form-control,
|
||||||
|
.form-select {
|
||||||
|
border: 2px solid var(--grey-200);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control:focus,
|
||||||
|
.form-select:focus {
|
||||||
|
border-color: var(--primary-blue);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(37, 99, 235, 0.1);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--grey-700);
|
||||||
|
margin-bottom: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table Styling */
|
||||||
|
.table {
|
||||||
|
background: white;
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead th {
|
||||||
|
background: var(--grey-50);
|
||||||
|
border-bottom: 2px solid var(--grey-200);
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--grey-700);
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody td {
|
||||||
|
padding: 1rem;
|
||||||
|
border-bottom: 1px solid var(--grey-100);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr:hover {
|
||||||
|
background: var(--grey-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status Badges */
|
||||||
|
.badge {
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-success {
|
||||||
|
background-color: var(--success-green) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-warning {
|
||||||
|
background-color: var(--warning-orange) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-danger {
|
||||||
|
background-color: var(--danger-red) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-info {
|
||||||
|
background-color: var(--primary-blue) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page Headers */
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
color: var(--grey-900);
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.container-fluid {
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
margin-bottom: var(--spacing-lg);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Table - Stack on Small Screens */
|
||||||
|
.table-responsive {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody,
|
||||||
|
.table tbody tr,
|
||||||
|
.table tbody td {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid var(--grey-200);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
margin-bottom: var(--spacing-md);
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody td {
|
||||||
|
border: none;
|
||||||
|
padding: var(--spacing-sm) 0;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody td:before {
|
||||||
|
content: attr(data-label) ": ";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: 35%;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--grey-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Navigation */
|
||||||
|
.navbar-toggler {
|
||||||
|
border: none;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-collapse {
|
||||||
|
margin-top: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav .nav-link {
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
margin: var(--spacing-xs) 0;
|
||||||
|
background: var(--grey-50);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern Form Layout */
|
||||||
|
.row .col-md-6 .form-group,
|
||||||
|
.row .col-md-6 .mb-3 {
|
||||||
|
margin-bottom: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action Buttons */
|
||||||
|
.d-flex.justify-content-between .btn {
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading States */
|
||||||
|
.btn:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Focus States for Accessibility */
|
||||||
|
.btn:focus,
|
||||||
|
.form-control:focus,
|
||||||
|
.form-select:focus {
|
||||||
|
outline: 2px solid var(--primary-blue);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toast/Alert Improvements */
|
||||||
|
.alert {
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border-left: 4px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background: #ecfdf5;
|
||||||
|
color: #065f46;
|
||||||
|
border-left-color: var(--success-green);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
background: #fef2f2;
|
||||||
|
color: #991b1b;
|
||||||
|
border-left-color: var(--danger-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-warning {
|
||||||
|
background: #fffbeb;
|
||||||
|
color: #92400e;
|
||||||
|
border-left-color: var(--warning-orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean List Styling */
|
||||||
|
.list-group-item {
|
||||||
|
border: 1px solid var(--grey-200);
|
||||||
|
background: white;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item:hover {
|
||||||
|
background: var(--grey-50);
|
||||||
|
transform: translateX(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item-action {
|
||||||
|
color: var(--grey-700);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item-action:hover {
|
||||||
|
color: var(--primary-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typography Improvements */
|
||||||
|
.text-muted {
|
||||||
|
color: var(--grey-500) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: var(--grey-100);
|
||||||
|
color: var(--grey-800);
|
||||||
|
padding: 0.125rem 0.375rem;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utility Classes */
|
||||||
|
.rounded-modern {
|
||||||
|
border-radius: var(--radius-lg) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-modern {
|
||||||
|
box-shadow: var(--shadow-md) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gradient-primary {
|
||||||
|
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-purple) 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print Styles */
|
||||||
|
@media print {
|
||||||
|
.navbar,
|
||||||
|
.btn,
|
||||||
|
.card-header {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border: 1px solid #ccc !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
534
LittleShop/wwwroot/css/radzen-tech-theme.css
Normal file
@ -0,0 +1,534 @@
|
|||||||
|
/*
|
||||||
|
* 🤖 RADZEN TECH HOLOGRAPHIC THEME 🤖
|
||||||
|
* Dark Purple Metallic with Holographic Effects
|
||||||
|
* Pixel Perfect Robot/Tech Aesthetic
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Core Theme Colors */
|
||||||
|
--tech-primary: #8A2BE2; /* Blue Violet */
|
||||||
|
--tech-secondary: #9932CC; /* Dark Orchid */
|
||||||
|
--tech-accent: #DA70D6; /* Orchid */
|
||||||
|
--tech-highlight: #FF00FF; /* Magenta */
|
||||||
|
--tech-neon: #00FFFF; /* Cyan */
|
||||||
|
|
||||||
|
/* Dark Base Colors */
|
||||||
|
--tech-dark-base: #0D0014; /* Ultra Dark Purple */
|
||||||
|
--tech-dark-surface: #1A0A2E; /* Dark Purple */
|
||||||
|
--tech-dark-card: #16213E; /* Dark Blue Purple */
|
||||||
|
--tech-dark-accent: #0F3460; /* Deep Blue */
|
||||||
|
|
||||||
|
/* Metallic Colors */
|
||||||
|
--tech-metallic-silver: #C0C0C0;
|
||||||
|
--tech-metallic-gold: #FFD700;
|
||||||
|
--tech-metallic-copper: #B87333;
|
||||||
|
--tech-metallic-chrome: #E5E5E5;
|
||||||
|
|
||||||
|
/* Holographic Gradient */
|
||||||
|
--holographic-gradient: linear-gradient(45deg,
|
||||||
|
#FF0080 0%,
|
||||||
|
#7928CA 25%,
|
||||||
|
#0070F3 50%,
|
||||||
|
#00DFD8 75%,
|
||||||
|
#FF0080 100%);
|
||||||
|
|
||||||
|
/* Glow Effects */
|
||||||
|
--tech-glow-primary: 0 0 20px rgba(138, 43, 226, 0.8);
|
||||||
|
--tech-glow-secondary: 0 0 30px rgba(153, 50, 204, 0.6);
|
||||||
|
--tech-glow-accent: 0 0 15px rgba(218, 112, 214, 0.9);
|
||||||
|
--tech-glow-neon: 0 0 25px rgba(0, 255, 255, 0.7);
|
||||||
|
|
||||||
|
/* Animation Timings */
|
||||||
|
--tech-transition-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
--tech-transition-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
--tech-transition-glow: 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Holographic Animation Keyframes */
|
||||||
|
@keyframes holographic-shift {
|
||||||
|
0% { background-position: 0% 50%; }
|
||||||
|
25% { background-position: 100% 50%; }
|
||||||
|
50% { background-position: 100% 0%; }
|
||||||
|
75% { background-position: 0% 0%; }
|
||||||
|
100% { background-position: 0% 50%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes tech-pulse {
|
||||||
|
0%, 100% {
|
||||||
|
transform: scale(1);
|
||||||
|
box-shadow: var(--tech-glow-primary);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: var(--tech-glow-secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes neon-flicker {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
2% { opacity: 0.8; }
|
||||||
|
4% { opacity: 1; }
|
||||||
|
8% { opacity: 0.9; }
|
||||||
|
10% { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes data-stream {
|
||||||
|
0% { transform: translateY(-100%); opacity: 0; }
|
||||||
|
10% { opacity: 1; }
|
||||||
|
90% { opacity: 1; }
|
||||||
|
100% { transform: translateY(100vh); opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global Dark Theme Base */
|
||||||
|
body {
|
||||||
|
background: var(--tech-dark-base) !important;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(circle at 25% 25%, rgba(138, 43, 226, 0.1) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 75% 75%, rgba(153, 50, 204, 0.1) 0%, transparent 50%),
|
||||||
|
linear-gradient(45deg, transparent 45%, rgba(0, 255, 255, 0.03) 50%, transparent 55%);
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
font-family: 'Segoe UI', 'Roboto Mono', 'Courier New', monospace !important;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radzen Component Overrides */
|
||||||
|
.rz-card {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
var(--tech-dark-surface) 0%,
|
||||||
|
var(--tech-dark-card) 100%) !important;
|
||||||
|
border: 1px solid rgba(138, 43, 226, 0.3) !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
box-shadow:
|
||||||
|
0 8px 32px rgba(13, 0, 20, 0.8),
|
||||||
|
inset 0 1px 0 rgba(218, 112, 214, 0.2),
|
||||||
|
0 0 0 1px rgba(138, 43, 226, 0.1) !important;
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
transition: all var(--tech-transition-smooth) !important;
|
||||||
|
backdrop-filter: blur(20px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-card:hover {
|
||||||
|
transform: translateY(-2px) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 16px 64px rgba(13, 0, 20, 0.9),
|
||||||
|
inset 0 1px 0 rgba(218, 112, 214, 0.4),
|
||||||
|
var(--tech-glow-primary) !important;
|
||||||
|
border-color: var(--tech-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-card-header {
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
rgba(138, 43, 226, 0.2) 0%,
|
||||||
|
rgba(153, 50, 204, 0.1) 100%) !important;
|
||||||
|
border-bottom: 1px solid rgba(218, 112, 214, 0.3) !important;
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
padding: 1rem 1.5rem !important;
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-card-header::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--holographic-gradient);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: holographic-shift 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-card-body {
|
||||||
|
padding: 1.5rem !important;
|
||||||
|
background: rgba(22, 33, 62, 0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radzen Buttons */
|
||||||
|
.rz-button {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
var(--tech-primary) 0%,
|
||||||
|
var(--tech-secondary) 100%) !important;
|
||||||
|
border: 1px solid var(--tech-accent) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
color: white !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
letter-spacing: 0.5px !important;
|
||||||
|
transition: all var(--tech-transition-smooth) !important;
|
||||||
|
position: relative !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-button::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
left: -2px;
|
||||||
|
right: -2px;
|
||||||
|
bottom: -2px;
|
||||||
|
background: var(--holographic-gradient);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
border-radius: inherit;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity var(--tech-transition-smooth);
|
||||||
|
animation: holographic-shift 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-button:hover::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-button:hover {
|
||||||
|
transform: translateY(-1px) !important;
|
||||||
|
/* box-shadow: var(--tech-glow-primary) !important; */
|
||||||
|
/* text-shadow: 0 0 10px rgba(255, 255, 255, 0.8) !important; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-button:active {
|
||||||
|
transform: scale(0.98) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-button-primary {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
var(--tech-primary) 0%,
|
||||||
|
var(--tech-highlight) 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-button-secondary {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
var(--tech-dark-accent) 0%,
|
||||||
|
var(--tech-secondary) 100%) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radzen Data Grid */
|
||||||
|
.rz-datatable,
|
||||||
|
.rz-grid {
|
||||||
|
background: var(--tech-dark-surface) !important;
|
||||||
|
border: 1px solid rgba(138, 43, 226, 0.3) !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-datatable-header,
|
||||||
|
.rz-grid-header {
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
var(--tech-dark-card) 0%,
|
||||||
|
var(--tech-dark-accent) 100%) !important;
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
border-bottom: 2px solid var(--tech-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-datatable-header th,
|
||||||
|
.rz-grid-header th {
|
||||||
|
border-right: 1px solid rgba(138, 43, 226, 0.2) !important;
|
||||||
|
padding: 1rem !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
letter-spacing: 0.5px !important;
|
||||||
|
font-size: 0.85rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-datatable-data tr,
|
||||||
|
.rz-grid-table tr {
|
||||||
|
background: rgba(26, 10, 46, 0.5) !important;
|
||||||
|
border-bottom: 1px solid rgba(218, 112, 214, 0.1) !important;
|
||||||
|
transition: all var(--tech-transition-fast) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-datatable-data tr:hover,
|
||||||
|
.rz-grid-table tr:hover {
|
||||||
|
background: rgba(138, 43, 226, 0.1) !important;
|
||||||
|
box-shadow: inset 0 0 20px rgba(218, 112, 214, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-datatable-data td,
|
||||||
|
.rz-grid-table td {
|
||||||
|
border-right: 1px solid rgba(138, 43, 226, 0.1) !important;
|
||||||
|
padding: 1rem !important;
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radzen Form Controls */
|
||||||
|
.rz-textbox,
|
||||||
|
.rz-dropdown,
|
||||||
|
.rz-multiselect,
|
||||||
|
.rz-textarea {
|
||||||
|
background: rgba(22, 33, 62, 0.8) !important;
|
||||||
|
border: 2px solid rgba(138, 43, 226, 0.3) !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
padding: 0.75rem 1rem !important;
|
||||||
|
transition: all var(--tech-transition-smooth) !important;
|
||||||
|
backdrop-filter: blur(10px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-textbox:focus,
|
||||||
|
.rz-dropdown:focus,
|
||||||
|
.rz-multiselect:focus,
|
||||||
|
.rz-textarea:focus {
|
||||||
|
border-color: var(--tech-primary) !important;
|
||||||
|
box-shadow:
|
||||||
|
var(--tech-glow-primary),
|
||||||
|
inset 0 0 20px rgba(138, 43, 226, 0.1) !important;
|
||||||
|
outline: none !important;
|
||||||
|
background: rgba(22, 33, 62, 1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-textbox::placeholder,
|
||||||
|
.rz-dropdown::placeholder,
|
||||||
|
.rz-textarea::placeholder {
|
||||||
|
color: rgba(192, 192, 192, 0.6) !important;
|
||||||
|
font-style: italic !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radzen Navigation */
|
||||||
|
.rz-navigation {
|
||||||
|
background: linear-gradient(180deg,
|
||||||
|
var(--tech-dark-surface) 0%,
|
||||||
|
var(--tech-dark-base) 100%) !important;
|
||||||
|
border-right: 1px solid rgba(138, 43, 226, 0.3) !important;
|
||||||
|
backdrop-filter: blur(20px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-navigation-item {
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
padding: 0.75rem 1.5rem !important;
|
||||||
|
transition: all var(--tech-transition-fast) !important;
|
||||||
|
border-radius: 0 25px 25px 0 !important;
|
||||||
|
margin: 0.25rem 0 !important;
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-navigation-item:hover {
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
rgba(138, 43, 226, 0.2) 0%,
|
||||||
|
rgba(153, 50, 204, 0.1) 100%) !important;
|
||||||
|
color: var(--tech-accent) !important;
|
||||||
|
transform: translateX(5px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-navigation-item.rz-state-active {
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
var(--tech-primary) 0%,
|
||||||
|
var(--tech-secondary) 100%) !important;
|
||||||
|
color: white !important;
|
||||||
|
box-shadow: var(--tech-glow-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-navigation-item.rz-state-active::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: -1px;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 3px;
|
||||||
|
background: var(--tech-neon);
|
||||||
|
box-shadow: var(--tech-glow-neon);
|
||||||
|
animation: neon-flicker 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radzen Panels */
|
||||||
|
.rz-panel {
|
||||||
|
background: var(--tech-dark-surface) !important;
|
||||||
|
border: 1px solid rgba(138, 43, 226, 0.3) !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
box-shadow: 0 8px 32px rgba(13, 0, 20, 0.6) !important;
|
||||||
|
backdrop-filter: blur(20px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-panel-header {
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
var(--tech-dark-card) 0%,
|
||||||
|
var(--tech-dark-accent) 100%) !important;
|
||||||
|
border-bottom: 1px solid rgba(218, 112, 214, 0.3) !important;
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
padding: 1rem 1.5rem !important;
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radzen Dialogs */
|
||||||
|
.rz-dialog {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
var(--tech-dark-surface) 0%,
|
||||||
|
var(--tech-dark-card) 100%) !important;
|
||||||
|
border: 2px solid var(--tech-primary) !important;
|
||||||
|
border-radius: 16px !important;
|
||||||
|
box-shadow:
|
||||||
|
0 20px 60px rgba(13, 0, 20, 0.9),
|
||||||
|
var(--tech-glow-primary) !important;
|
||||||
|
backdrop-filter: blur(30px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-dialog-header {
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
rgba(138, 43, 226, 0.3) 0%,
|
||||||
|
rgba(153, 50, 204, 0.2) 100%) !important;
|
||||||
|
border-bottom: 1px solid rgba(218, 112, 214, 0.4) !important;
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
letter-spacing: 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radzen Notifications */
|
||||||
|
.rz-notification {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
var(--tech-dark-surface) 0%,
|
||||||
|
var(--tech-dark-card) 100%) !important;
|
||||||
|
border: 1px solid var(--tech-primary) !important;
|
||||||
|
border-radius: 12px !important;
|
||||||
|
color: var(--tech-metallic-chrome) !important;
|
||||||
|
box-shadow:
|
||||||
|
0 8px 32px rgba(13, 0, 20, 0.8),
|
||||||
|
var(--tech-glow-primary) !important;
|
||||||
|
backdrop-filter: blur(20px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-notification-success {
|
||||||
|
border-color: var(--tech-neon) !important;
|
||||||
|
box-shadow: var(--tech-glow-neon) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-notification-error {
|
||||||
|
border-color: var(--tech-highlight) !important;
|
||||||
|
box-shadow: 0 0 25px rgba(255, 0, 255, 0.7) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Holographic Border Effect */
|
||||||
|
.tech-holographic-border {
|
||||||
|
position: relative;
|
||||||
|
border: 2px solid transparent !important;
|
||||||
|
background: linear-gradient(var(--tech-dark-surface), var(--tech-dark-surface)) padding-box,
|
||||||
|
var(--holographic-gradient) border-box !important;
|
||||||
|
background-size: 200% 200%;
|
||||||
|
animation: holographic-shift 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tech Grid Pattern Overlay */
|
||||||
|
.tech-grid-overlay {
|
||||||
|
background-image:
|
||||||
|
linear-gradient(rgba(138, 43, 226, 0.1) 1px, transparent 1px),
|
||||||
|
linear-gradient(90deg, rgba(138, 43, 226, 0.1) 1px, transparent 1px);
|
||||||
|
background-size: 20px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar Styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 12px;
|
||||||
|
background: var(--tech-dark-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: rgba(26, 10, 46, 0.5);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: linear-gradient(180deg,
|
||||||
|
var(--tech-primary) 0%,
|
||||||
|
var(--tech-secondary) 100%);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid rgba(218, 112, 214, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: linear-gradient(180deg,
|
||||||
|
var(--tech-accent) 0%,
|
||||||
|
var(--tech-highlight) 100%);
|
||||||
|
box-shadow: var(--tech-glow-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading Spinner Override */
|
||||||
|
.rz-spinner {
|
||||||
|
border: 3px solid rgba(138, 43, 226, 0.3);
|
||||||
|
border-top: 3px solid var(--tech-primary);
|
||||||
|
box-shadow: var(--tech-glow-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Responsive Adjustments */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.rz-card {
|
||||||
|
margin: 0.5rem !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-button {
|
||||||
|
padding: 0.75rem 1.5rem !important;
|
||||||
|
font-size: 0.9rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-navigation-item {
|
||||||
|
padding: 1rem !important;
|
||||||
|
border-radius: 0 20px 20px 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print Styles */
|
||||||
|
@media print {
|
||||||
|
body {
|
||||||
|
background: white !important;
|
||||||
|
color: black !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rz-card {
|
||||||
|
background: white !important;
|
||||||
|
border: 1px solid #ccc !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* High Contrast Mode */
|
||||||
|
@media (prefers-contrast: high) {
|
||||||
|
:root {
|
||||||
|
--tech-primary: #FF00FF;
|
||||||
|
--tech-secondary: #00FFFF;
|
||||||
|
--tech-accent: #FFFF00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduced Motion */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
* {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark Theme Class for Radzen */
|
||||||
|
.dark-theme {
|
||||||
|
--rz-primary: var(--tech-primary);
|
||||||
|
--rz-secondary: var(--tech-secondary);
|
||||||
|
--rz-success: var(--tech-neon);
|
||||||
|
--rz-info: var(--tech-accent);
|
||||||
|
--rz-warning: var(--tech-metallic-gold);
|
||||||
|
--rz-danger: var(--tech-highlight);
|
||||||
|
--rz-dark: var(--tech-dark-base);
|
||||||
|
--rz-light: var(--tech-metallic-chrome);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tech Enhancement Classes */
|
||||||
|
.tech-glow {
|
||||||
|
box-shadow: var(--tech-glow-primary) !important;
|
||||||
|
animation: tech-pulse 2s infinite !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-holographic {
|
||||||
|
background: var(--holographic-gradient) !important;
|
||||||
|
background-size: 200% 200% !important;
|
||||||
|
animation: holographic-shift 3s ease-in-out infinite !important;
|
||||||
|
-webkit-background-clip: text !important;
|
||||||
|
background-clip: text !important;
|
||||||
|
color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-neon {
|
||||||
|
color: var(--tech-neon) !important;
|
||||||
|
text-shadow: var(--tech-glow-neon) !important;
|
||||||
|
animation: neon-flicker 2s infinite !important;
|
||||||
|
}
|
||||||
1
LittleShop/wwwroot/favicon.ico
Normal file
@ -0,0 +1 @@
|
|||||||
|
AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
BIN
LittleShop/wwwroot/icons/icon-128x128.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
LittleShop/wwwroot/icons/icon-144x144.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
LittleShop/wwwroot/icons/icon-152x152.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
LittleShop/wwwroot/icons/icon-192x192.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
LittleShop/wwwroot/icons/icon-384x384.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
LittleShop/wwwroot/icons/icon-512x512.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
LittleShop/wwwroot/icons/icon-72x72.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
LittleShop/wwwroot/icons/icon-96x96.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
16
LittleShop/wwwroot/icons/icon-placeholder.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||||
|
<stop offset="0%" style="stop-color:#2563eb;stop-opacity:1" />
|
||||||
|
<stop offset="100%" style="stop-color:#7c3aed;stop-opacity:1" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<rect width="512" height="512" fill="url(#gradient)" rx="80"/>
|
||||||
|
<g fill="white">
|
||||||
|
<path d="M256 120c75.111 0 136 60.889 136 136s-60.889 136-136 136-136-60.889-136-136 60.889-136 136-136zm0 32c-57.438 0-104 46.562-104 104s46.562 104 104 104 104-46.562 104-104-46.562-104-104-104z"/>
|
||||||
|
<circle cx="256" cy="256" r="48"/>
|
||||||
|
<path d="M160 320h192v32H160z"/>
|
||||||
|
<path d="M192 360h128v24H192z"/>
|
||||||
|
</g>
|
||||||
|
<text x="256" y="420" text-anchor="middle" fill="white" font-family="system-ui" font-size="32" font-weight="600">LS</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 879 B |
537
LittleShop/wwwroot/js/holographic-effects.js
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
// 🤖 Holographic Tech Robot Effects System 🤖
|
||||||
|
// Advanced pixel-perfect holographic animations and tech aesthetics
|
||||||
|
|
||||||
|
class HolographicEffectsSystem {
|
||||||
|
constructor() {
|
||||||
|
this.isInitialized = false;
|
||||||
|
this.effectsActive = true;
|
||||||
|
this.dataStreams = [];
|
||||||
|
this.glitchElements = [];
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if (this.isInitialized) return;
|
||||||
|
|
||||||
|
console.log('🤖 Initializing Holographic Effects System...');
|
||||||
|
|
||||||
|
// Initialize effects when DOM is ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => this.setupEffects());
|
||||||
|
} else {
|
||||||
|
this.setupEffects();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEffects() {
|
||||||
|
// this.createDataStreamBackground(); // Removed
|
||||||
|
// this.setupHolographicBorders(); // Removed
|
||||||
|
this.initializeGlitchEffects();
|
||||||
|
this.createParticleSystem();
|
||||||
|
this.setupTechScanlines();
|
||||||
|
this.initializeRobotAnimations();
|
||||||
|
// this.setupInteractiveHovers(); // Removed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create animated data streams in background
|
||||||
|
createDataStreamBackground() {
|
||||||
|
const dataStreamContainer = document.createElement('div');
|
||||||
|
dataStreamContainer.className = 'data-stream-container';
|
||||||
|
dataStreamContainer.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Create multiple data streams
|
||||||
|
for (let i = 0; i < 8; i++) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.createDataStream(dataStreamContainer, i);
|
||||||
|
}, i * 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.appendChild(dataStreamContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
createDataStream(container, index) {
|
||||||
|
const stream = document.createElement('div');
|
||||||
|
stream.className = 'data-stream';
|
||||||
|
|
||||||
|
const left = Math.random() * 100;
|
||||||
|
const animationDuration = 3 + Math.random() * 4;
|
||||||
|
const delay = Math.random() * 2;
|
||||||
|
|
||||||
|
stream.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
left: ${left}%;
|
||||||
|
width: 2px;
|
||||||
|
height: 100px;
|
||||||
|
background: linear-gradient(to bottom,
|
||||||
|
transparent 0%,
|
||||||
|
rgba(0, 255, 255, 0.8) 20%,
|
||||||
|
rgba(138, 43, 226, 0.6) 80%,
|
||||||
|
transparent 100%);
|
||||||
|
animation: dataStreamFlow ${animationDuration}s linear infinite;
|
||||||
|
animation-delay: ${delay}s;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Add random tech symbols
|
||||||
|
const symbols = ['01', '10', '11', '00', 'Ω', '∆', '∇', '◊', '◈'];
|
||||||
|
const symbol = document.createElement('span');
|
||||||
|
symbol.textContent = symbols[Math.floor(Math.random() * symbols.length)];
|
||||||
|
symbol.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: -5px;
|
||||||
|
color: rgba(0, 255, 255, 0.8);
|
||||||
|
font-size: 8px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
text-shadow: 0 0 5px rgba(0, 255, 255, 0.8);
|
||||||
|
`;
|
||||||
|
|
||||||
|
stream.appendChild(symbol);
|
||||||
|
container.appendChild(stream);
|
||||||
|
|
||||||
|
// Remove and recreate after animation
|
||||||
|
setTimeout(() => {
|
||||||
|
if (stream.parentNode) {
|
||||||
|
stream.parentNode.removeChild(stream);
|
||||||
|
}
|
||||||
|
if (this.effectsActive) {
|
||||||
|
this.createDataStream(container, index);
|
||||||
|
}
|
||||||
|
}, (animationDuration + delay) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup holographic borders for cards
|
||||||
|
setupHolographicBorders() {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = `
|
||||||
|
@keyframes dataStreamFlow {
|
||||||
|
0% {
|
||||||
|
transform: translateY(-100px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
10% { opacity: 1; }
|
||||||
|
90% { opacity: 1; }
|
||||||
|
100% {
|
||||||
|
transform: translateY(calc(100vh + 100px));
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes holographicBorderShift {
|
||||||
|
0% { border-image-source: linear-gradient(45deg, #8A2BE2, #9932CC, #DA70D6, #FF00FF); }
|
||||||
|
25% { border-image-source: linear-gradient(90deg, #00FFFF, #8A2BE2, #9932CC, #DA70D6); }
|
||||||
|
50% { border-image-source: linear-gradient(135deg, #FF00FF, #00FFFF, #8A2BE2, #9932CC); }
|
||||||
|
75% { border-image-source: linear-gradient(180deg, #DA70D6, #FF00FF, #00FFFF, #8A2BE2); }
|
||||||
|
100% { border-image-source: linear-gradient(45deg, #8A2BE2, #9932CC, #DA70D6, #FF00FF); }
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
// Apply to cards
|
||||||
|
setTimeout(() => {
|
||||||
|
const cards = document.querySelectorAll('.rz-card, .card, .mobile-card');
|
||||||
|
cards.forEach(card => {
|
||||||
|
card.style.borderImage = 'linear-gradient(45deg, #8A2BE2, #9932CC, #DA70D6, #FF00FF) 1';
|
||||||
|
card.style.animation = 'holographicBorderShift 4s ease-in-out infinite';
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize glitch effects on hover
|
||||||
|
initializeGlitchEffects() {
|
||||||
|
const glitchStyle = document.createElement('style');
|
||||||
|
glitchStyle.textContent = `
|
||||||
|
.tech-glitch {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-glitch::before,
|
||||||
|
.tech-glitch::after {
|
||||||
|
content: attr(data-text);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-glitch::before {
|
||||||
|
color: #FF00FF;
|
||||||
|
animation: glitch1 0.5s infinite;
|
||||||
|
clip: rect(0, 900px, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-glitch::after {
|
||||||
|
color: #00FFFF;
|
||||||
|
animation: glitch2 0.5s infinite;
|
||||||
|
clip: rect(0, 900px, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-glitch:hover::before,
|
||||||
|
.tech-glitch:hover::after {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glitch1 {
|
||||||
|
0% { clip: rect(42px, 9999px, 44px, 0); }
|
||||||
|
20% { clip: rect(12px, 9999px, 59px, 0); }
|
||||||
|
40% { clip: rect(63px, 9999px, 34px, 0); }
|
||||||
|
60% { clip: rect(18px, 9999px, 76px, 0); }
|
||||||
|
80% { clip: rect(54px, 9999px, 91px, 0); }
|
||||||
|
100% { clip: rect(25px, 9999px, 38px, 0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glitch2 {
|
||||||
|
0% { clip: rect(65px, 9999px, 23px, 0); }
|
||||||
|
20% { clip: rect(87px, 9999px, 45px, 0); }
|
||||||
|
40% { clip: rect(29px, 9999px, 78px, 0); }
|
||||||
|
60% { clip: rect(52px, 9999px, 31px, 0); }
|
||||||
|
80% { clip: rect(15px, 9999px, 89px, 0); }
|
||||||
|
100% { clip: rect(73px, 9999px, 16px, 0); }
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(glitchStyle);
|
||||||
|
|
||||||
|
// Apply to headings
|
||||||
|
setTimeout(() => {
|
||||||
|
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||||
|
headings.forEach(heading => {
|
||||||
|
heading.classList.add('tech-glitch');
|
||||||
|
heading.setAttribute('data-text', heading.textContent);
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create particle system
|
||||||
|
createParticleSystem() {
|
||||||
|
const particleContainer = document.createElement('div');
|
||||||
|
particleContainer.className = 'particle-system';
|
||||||
|
particleContainer.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -2;
|
||||||
|
overflow: hidden;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Create floating particles
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.createParticle(particleContainer);
|
||||||
|
}, i * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.appendChild(particleContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
createParticle(container) {
|
||||||
|
const particle = document.createElement('div');
|
||||||
|
const size = Math.random() * 3 + 1;
|
||||||
|
const x = Math.random() * window.innerWidth;
|
||||||
|
const y = Math.random() * window.innerHeight;
|
||||||
|
const duration = Math.random() * 20 + 10;
|
||||||
|
|
||||||
|
particle.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
width: ${size}px;
|
||||||
|
height: ${size}px;
|
||||||
|
background: radial-gradient(circle, rgba(138, 43, 226, 0.8) 0%, transparent 70%);
|
||||||
|
left: ${x}px;
|
||||||
|
top: ${y}px;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: particleFloat ${duration}s linear infinite;
|
||||||
|
box-shadow: 0 0 ${size * 3}px rgba(138, 43, 226, 0.6);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const floatDistance = Math.random() * 100 + 50;
|
||||||
|
const angle = Math.random() * 360;
|
||||||
|
|
||||||
|
particle.style.setProperty('--float-x', `${Math.cos(angle * Math.PI / 180) * floatDistance}px`);
|
||||||
|
particle.style.setProperty('--float-y', `${Math.sin(angle * Math.PI / 180) * floatDistance}px`);
|
||||||
|
|
||||||
|
container.appendChild(particle);
|
||||||
|
|
||||||
|
// Remove after animation
|
||||||
|
setTimeout(() => {
|
||||||
|
if (particle.parentNode) {
|
||||||
|
particle.parentNode.removeChild(particle);
|
||||||
|
}
|
||||||
|
if (this.effectsActive) {
|
||||||
|
this.createParticle(container);
|
||||||
|
}
|
||||||
|
}, duration * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup tech scanlines
|
||||||
|
setupTechScanlines() {
|
||||||
|
const scanlineStyle = document.createElement('style');
|
||||||
|
scanlineStyle.textContent = `
|
||||||
|
@keyframes particleFloat {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translate(0, 0) scale(1);
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translate(var(--float-x), var(--float-y)) scale(1.5);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-scanlines::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
0deg,
|
||||||
|
transparent,
|
||||||
|
transparent 2px,
|
||||||
|
rgba(0, 255, 255, 0.03) 2px,
|
||||||
|
rgba(0, 255, 255, 0.03) 4px
|
||||||
|
);
|
||||||
|
pointer-events: none;
|
||||||
|
animation: scanlineMove 2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scanlineMove {
|
||||||
|
0% { transform: translateY(0); }
|
||||||
|
100% { transform: translateY(4px); }
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(scanlineStyle);
|
||||||
|
|
||||||
|
// Apply to main containers
|
||||||
|
setTimeout(() => {
|
||||||
|
const containers = document.querySelectorAll('.container, .container-fluid, .main-content');
|
||||||
|
containers.forEach(container => {
|
||||||
|
container.classList.add('tech-scanlines');
|
||||||
|
container.style.position = 'relative';
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize robot-style animations
|
||||||
|
initializeRobotAnimations() {
|
||||||
|
const robotStyle = document.createElement('style');
|
||||||
|
robotStyle.textContent = `
|
||||||
|
.robot-pulse {
|
||||||
|
animation: robotPulse 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.robot-scan {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.robot-scan::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
left: -50%;
|
||||||
|
width: 200%;
|
||||||
|
height: 200%;
|
||||||
|
background: linear-gradient(45deg,
|
||||||
|
transparent 40%,
|
||||||
|
rgba(0, 255, 255, 0.1) 50%,
|
||||||
|
transparent 60%);
|
||||||
|
animation: robotScan 4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes robotPulse {
|
||||||
|
0%, 100% {
|
||||||
|
transform: scale(1);
|
||||||
|
box-shadow: 0 0 20px rgba(138, 43, 226, 0.3);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.02);
|
||||||
|
box-shadow: 0 0 40px rgba(138, 43, 226, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes robotScan {
|
||||||
|
0% { transform: translate(-100%, -100%) rotate(0deg); }
|
||||||
|
100% { transform: translate(100%, 100%) rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-loading {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tech-loading::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -2px;
|
||||||
|
left: -2px;
|
||||||
|
right: -2px;
|
||||||
|
bottom: -2px;
|
||||||
|
background: linear-gradient(45deg, #8A2BE2, #00FFFF, #8A2BE2);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
border-radius: inherit;
|
||||||
|
z-index: -1;
|
||||||
|
animation: techLoadingPulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes techLoadingPulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.3;
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.8;
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(robotStyle);
|
||||||
|
|
||||||
|
// Apply to cards only (removed button glow effects)
|
||||||
|
setTimeout(() => {
|
||||||
|
// const buttons = document.querySelectorAll('.rz-button, .btn');
|
||||||
|
// buttons.forEach(button => {
|
||||||
|
// button.classList.add('robot-pulse');
|
||||||
|
// });
|
||||||
|
|
||||||
|
const cards = document.querySelectorAll('.rz-card, .card');
|
||||||
|
cards.forEach(card => {
|
||||||
|
card.classList.add('robot-scan');
|
||||||
|
});
|
||||||
|
}, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup interactive hover effects
|
||||||
|
setupInteractiveHovers() {
|
||||||
|
setTimeout(() => {
|
||||||
|
// Add hover effects to interactive elements
|
||||||
|
const interactiveElements = document.querySelectorAll('button, .rz-button, .btn, a, .card');
|
||||||
|
|
||||||
|
interactiveElements.forEach(element => {
|
||||||
|
element.addEventListener('mouseenter', (e) => {
|
||||||
|
this.createHoverEffect(e.target);
|
||||||
|
});
|
||||||
|
|
||||||
|
element.addEventListener('mouseleave', (e) => {
|
||||||
|
this.removeHoverEffect(e.target);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
createHoverEffect(element) {
|
||||||
|
// Create ripple effect
|
||||||
|
const ripple = document.createElement('div');
|
||||||
|
ripple.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle, rgba(0, 255, 255, 0.6) 0%, transparent 70%);
|
||||||
|
transform: scale(0);
|
||||||
|
animation: rippleEffect 0.6s linear;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1000;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const size = Math.max(rect.width, rect.height);
|
||||||
|
ripple.style.width = ripple.style.height = size + 'px';
|
||||||
|
ripple.style.left = (rect.left + rect.width / 2 - size / 2) + 'px';
|
||||||
|
ripple.style.top = (rect.top + rect.height / 2 - size / 2) + 'px';
|
||||||
|
|
||||||
|
document.body.appendChild(ripple);
|
||||||
|
|
||||||
|
// Remove after animation
|
||||||
|
setTimeout(() => {
|
||||||
|
if (ripple.parentNode) {
|
||||||
|
ripple.parentNode.removeChild(ripple);
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
|
||||||
|
// Add tech glow to element
|
||||||
|
element.style.transition = 'all 0.3s ease';
|
||||||
|
element.style.boxShadow = '0 0 30px rgba(0, 255, 255, 0.8), inset 0 0 20px rgba(138, 43, 226, 0.4)';
|
||||||
|
element.style.transform = 'scale(1.05)';
|
||||||
|
|
||||||
|
if (!document.getElementById('ripple-style')) {
|
||||||
|
const rippleStyle = document.createElement('style');
|
||||||
|
rippleStyle.id = 'ripple-style';
|
||||||
|
rippleStyle.textContent = `
|
||||||
|
@keyframes rippleEffect {
|
||||||
|
to {
|
||||||
|
transform: scale(4);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(rippleStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeHoverEffect(element) {
|
||||||
|
element.style.boxShadow = '';
|
||||||
|
element.style.transform = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle effects on/off
|
||||||
|
toggleEffects() {
|
||||||
|
this.effectsActive = !this.effectsActive;
|
||||||
|
|
||||||
|
if (!this.effectsActive) {
|
||||||
|
// Remove all effect containers
|
||||||
|
const effectContainers = document.querySelectorAll('.data-stream-container, .particle-system');
|
||||||
|
effectContainers.forEach(container => {
|
||||||
|
if (container.parentNode) {
|
||||||
|
container.parentNode.removeChild(container);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Restart effects
|
||||||
|
this.setupEffects();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`🤖 Holographic effects ${this.effectsActive ? 'enabled' : 'disabled'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performance monitoring
|
||||||
|
checkPerformance() {
|
||||||
|
const start = performance.now();
|
||||||
|
setTimeout(() => {
|
||||||
|
const delta = performance.now() - start;
|
||||||
|
if (delta > 50) { // If frame time is too long
|
||||||
|
console.warn('🤖 Performance warning: Consider reducing effects');
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the system
|
||||||
|
const holographicSystem = new HolographicEffectsSystem();
|
||||||
|
|
||||||
|
// Expose control functions globally
|
||||||
|
window.toggleHolographicEffects = () => holographicSystem.toggleEffects();
|
||||||
|
window.holographicSystem = holographicSystem;
|
||||||
|
|
||||||
|
// Add keyboard shortcut (Ctrl+H) to toggle effects
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.ctrlKey && e.key === 'h') {
|
||||||
|
e.preventDefault();
|
||||||
|
holographicSystem.toggleEffects();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('🤖 Holographic Tech System loaded! Press Ctrl+H to toggle effects.');
|
||||||
214
LittleShop/wwwroot/js/modern-mobile.js
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// Modern Mobile Enhancements
|
||||||
|
// Clean, simple mobile-friendly functionality
|
||||||
|
|
||||||
|
class ModernMobile {
|
||||||
|
constructor() {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.setupMobileTableLabels();
|
||||||
|
this.setupResponsiveNavigation();
|
||||||
|
this.setupFormEnhancements();
|
||||||
|
this.setupSmoothInteractions();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add data labels for mobile table stacking
|
||||||
|
setupMobileTableLabels() {
|
||||||
|
const tables = document.querySelectorAll('.table');
|
||||||
|
|
||||||
|
tables.forEach(table => {
|
||||||
|
const headers = Array.from(table.querySelectorAll('thead th')).map(th => th.textContent.trim());
|
||||||
|
const rows = table.querySelectorAll('tbody tr');
|
||||||
|
|
||||||
|
rows.forEach(row => {
|
||||||
|
const cells = row.querySelectorAll('td');
|
||||||
|
cells.forEach((cell, index) => {
|
||||||
|
if (headers[index]) {
|
||||||
|
cell.setAttribute('data-label', headers[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enhanced mobile navigation
|
||||||
|
setupResponsiveNavigation() {
|
||||||
|
const navbar = document.querySelector('.navbar');
|
||||||
|
const toggler = document.querySelector('.navbar-toggler');
|
||||||
|
const collapse = document.querySelector('.navbar-collapse');
|
||||||
|
|
||||||
|
if (toggler && collapse) {
|
||||||
|
// Smooth collapse animation
|
||||||
|
toggler.addEventListener('click', () => {
|
||||||
|
collapse.classList.toggle('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menu when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!navbar.contains(e.target) && collapse.classList.contains('show')) {
|
||||||
|
collapse.classList.remove('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form enhancements for better mobile UX
|
||||||
|
setupFormEnhancements() {
|
||||||
|
// Auto-focus first input on desktop
|
||||||
|
if (window.innerWidth > 768) {
|
||||||
|
const firstInput = document.querySelector('.form-control:not([readonly]):not([disabled])');
|
||||||
|
if (firstInput) {
|
||||||
|
firstInput.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enhanced form validation feedback
|
||||||
|
const forms = document.querySelectorAll('form');
|
||||||
|
forms.forEach(form => {
|
||||||
|
form.addEventListener('submit', (e) => {
|
||||||
|
const invalidInputs = form.querySelectorAll(':invalid');
|
||||||
|
if (invalidInputs.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
invalidInputs[0].focus();
|
||||||
|
invalidInputs[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Floating labels effect
|
||||||
|
const inputs = document.querySelectorAll('.form-control, .form-select');
|
||||||
|
inputs.forEach(input => {
|
||||||
|
if (input.value) {
|
||||||
|
input.parentElement.classList.add('has-value');
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addEventListener('blur', () => {
|
||||||
|
if (input.value) {
|
||||||
|
input.parentElement.classList.add('has-value');
|
||||||
|
} else {
|
||||||
|
input.parentElement.classList.remove('has-value');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth interactions and feedback
|
||||||
|
setupSmoothInteractions() {
|
||||||
|
// Button click feedback
|
||||||
|
const buttons = document.querySelectorAll('.btn');
|
||||||
|
buttons.forEach(button => {
|
||||||
|
button.addEventListener('click', (e) => {
|
||||||
|
if (!button.disabled) {
|
||||||
|
button.style.transform = 'scale(0.95)';
|
||||||
|
setTimeout(() => {
|
||||||
|
button.style.transform = '';
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Card hover enhancement
|
||||||
|
const cards = document.querySelectorAll('.card');
|
||||||
|
cards.forEach(card => {
|
||||||
|
card.addEventListener('mouseenter', () => {
|
||||||
|
card.style.transform = 'translateY(-2px)';
|
||||||
|
});
|
||||||
|
|
||||||
|
card.addEventListener('mouseleave', () => {
|
||||||
|
card.style.transform = '';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Smooth scroll for anchor links
|
||||||
|
const anchorLinks = document.querySelectorAll('a[href^="#"]');
|
||||||
|
anchorLinks.forEach(link => {
|
||||||
|
link.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const target = document.querySelector(link.getAttribute('href'));
|
||||||
|
if (target) {
|
||||||
|
target.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show toast notification
|
||||||
|
showToast(message, type = 'info') {
|
||||||
|
const toastContainer = document.getElementById('toast-container') || this.createToastContainer();
|
||||||
|
|
||||||
|
const toast = document.createElement('div');
|
||||||
|
toast.className = `alert alert-${type} alert-dismissible fade show`;
|
||||||
|
toast.style.cssText = `
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
animation: slideInRight 0.3s ease;
|
||||||
|
`;
|
||||||
|
|
||||||
|
toast.innerHTML = `
|
||||||
|
${message}
|
||||||
|
<button type="button" class="btn-close" aria-label="Close"></button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
toastContainer.appendChild(toast);
|
||||||
|
|
||||||
|
// Auto-remove after 4 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.classList.add('fade');
|
||||||
|
setTimeout(() => {
|
||||||
|
if (toast.parentNode) {
|
||||||
|
toast.parentNode.removeChild(toast);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
}, 4000);
|
||||||
|
|
||||||
|
// Manual close
|
||||||
|
const closeBtn = toast.querySelector('.btn-close');
|
||||||
|
closeBtn.addEventListener('click', () => {
|
||||||
|
toast.classList.add('fade');
|
||||||
|
setTimeout(() => {
|
||||||
|
if (toast.parentNode) {
|
||||||
|
toast.parentNode.removeChild(toast);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createToastContainer() {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.id = 'toast-container';
|
||||||
|
container.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1050;
|
||||||
|
max-width: 300px;
|
||||||
|
`;
|
||||||
|
document.body.appendChild(container);
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS for toast animations
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = `
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade {
|
||||||
|
opacity: 0 !important;
|
||||||
|
transition: opacity 0.15s linear !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
const modernMobile = new ModernMobile();
|
||||||
|
window.modernMobile = modernMobile;
|
||||||
205
LittleShop/wwwroot/js/pwa.js
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
// Progressive Web App functionality
|
||||||
|
// Handles service worker registration and PWA features
|
||||||
|
|
||||||
|
class PWAManager {
|
||||||
|
constructor() {
|
||||||
|
this.swRegistration = null;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
console.log('PWA: Initializing PWA Manager...');
|
||||||
|
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
try {
|
||||||
|
this.swRegistration = await navigator.serviceWorker.register('/sw.js');
|
||||||
|
console.log('SW: Service Worker registered successfully');
|
||||||
|
|
||||||
|
// Listen for updates
|
||||||
|
this.swRegistration.addEventListener('updatefound', () => {
|
||||||
|
console.log('SW: New version available');
|
||||||
|
this.showUpdateNotification();
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('SW: Service Worker registration failed:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup PWA install prompt
|
||||||
|
this.setupInstallPrompt();
|
||||||
|
|
||||||
|
// Setup notifications (if enabled)
|
||||||
|
this.setupNotifications();
|
||||||
|
|
||||||
|
// Show manual install option after 3 seconds if no prompt appeared
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!document.getElementById('pwa-install-btn')) {
|
||||||
|
this.showManualInstallButton();
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupInstallPrompt() {
|
||||||
|
let deferredPrompt;
|
||||||
|
|
||||||
|
window.addEventListener('beforeinstallprompt', (e) => {
|
||||||
|
console.log('PWA: beforeinstallprompt event fired');
|
||||||
|
// Prevent Chrome 67 and earlier from automatically showing the prompt
|
||||||
|
e.preventDefault();
|
||||||
|
deferredPrompt = e;
|
||||||
|
|
||||||
|
// Show custom install button
|
||||||
|
this.showInstallButton(deferredPrompt);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('appinstalled', () => {
|
||||||
|
console.log('PWA: App was installed');
|
||||||
|
this.hideInstallButton();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Debug: Check if app is already installed
|
||||||
|
if (this.isInstalled()) {
|
||||||
|
console.log('PWA: App is already installed (standalone mode)');
|
||||||
|
} else {
|
||||||
|
console.log('PWA: App is not installed, waiting for install prompt...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showInstallButton(deferredPrompt) {
|
||||||
|
// Create install button
|
||||||
|
const installBtn = document.createElement('button');
|
||||||
|
installBtn.id = 'pwa-install-btn';
|
||||||
|
installBtn.className = 'btn btn-primary btn-sm';
|
||||||
|
installBtn.innerHTML = '<i class="fas fa-download"></i> Install App';
|
||||||
|
installBtn.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
|
||||||
|
`;
|
||||||
|
|
||||||
|
installBtn.addEventListener('click', async () => {
|
||||||
|
if (deferredPrompt) {
|
||||||
|
deferredPrompt.prompt();
|
||||||
|
const { outcome } = await deferredPrompt.userChoice;
|
||||||
|
console.log('PWA: User response to install prompt:', outcome);
|
||||||
|
deferredPrompt = null;
|
||||||
|
this.hideInstallButton();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(installBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideInstallButton() {
|
||||||
|
const installBtn = document.getElementById('pwa-install-btn');
|
||||||
|
if (installBtn) {
|
||||||
|
installBtn.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showUpdateNotification() {
|
||||||
|
// Show update available notification
|
||||||
|
const notification = document.createElement('div');
|
||||||
|
notification.className = 'alert alert-info alert-dismissible';
|
||||||
|
notification.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1050;
|
||||||
|
max-width: 300px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
notification.innerHTML = `
|
||||||
|
<strong>Update Available!</strong><br>
|
||||||
|
A new version of the app is ready.
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-info ms-2" id="update-btn">
|
||||||
|
Update Now
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
// Handle update
|
||||||
|
document.getElementById('update-btn').addEventListener('click', () => {
|
||||||
|
if (this.swRegistration && this.swRegistration.waiting) {
|
||||||
|
this.swRegistration.waiting.postMessage({ type: 'SKIP_WAITING' });
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async setupNotifications() {
|
||||||
|
// Check if notifications are supported and get permission
|
||||||
|
if ('Notification' in window) {
|
||||||
|
const permission = await this.requestNotificationPermission();
|
||||||
|
console.log('Notifications permission:', permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestNotificationPermission() {
|
||||||
|
if (Notification.permission === 'default') {
|
||||||
|
// Only request permission when user interacts with a relevant feature
|
||||||
|
// For now, just return the current status
|
||||||
|
return Notification.permission;
|
||||||
|
}
|
||||||
|
return Notification.permission;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show notification (if permission granted)
|
||||||
|
showNotification(title, options = {}) {
|
||||||
|
if (Notification.permission === 'granted') {
|
||||||
|
const notification = new Notification(title, {
|
||||||
|
icon: '/icons/icon-192x192.png',
|
||||||
|
badge: '/icons/icon-72x72.png',
|
||||||
|
tag: 'littleshop-admin',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-close after 5 seconds
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.close();
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show manual install button for browsers that don't auto-prompt
|
||||||
|
showManualInstallButton() {
|
||||||
|
console.log('PWA: Showing manual install button');
|
||||||
|
const installBtn = document.createElement('button');
|
||||||
|
installBtn.id = 'pwa-install-btn';
|
||||||
|
installBtn.className = 'btn btn-primary btn-sm';
|
||||||
|
installBtn.innerHTML = '<i class="fas fa-mobile-alt"></i> Install as App';
|
||||||
|
installBtn.style.cssText = `
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
|
||||||
|
`;
|
||||||
|
|
||||||
|
installBtn.addEventListener('click', () => {
|
||||||
|
alert('To install this app:\\n\\n1. Click the browser menu (⋮)\\n2. Select "Install LittleShop Admin"\\n\\nOr look for the install icon in the address bar!');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(installBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if app is installed
|
||||||
|
isInstalled() {
|
||||||
|
return window.matchMedia('(display-mode: standalone)').matches ||
|
||||||
|
window.navigator.standalone === true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize PWA Manager
|
||||||
|
const pwaManager = new PWAManager();
|
||||||
|
window.pwaManager = pwaManager;
|
||||||
|
|
||||||
|
// Expose notification function globally
|
||||||
|
window.showNotification = (title, options) => pwaManager.showNotification(title, options);
|
||||||
2018
LittleShop/wwwroot/lib/bootstrap-icons/bootstrap-icons.css
vendored
Normal file
7
LittleShop/wwwroot/lib/bootstrap/css/bootstrap.min.css
vendored
Normal file
7
LittleShop/wwwroot/lib/bootstrap/js/bootstrap.bundle.min.js
vendored
Normal file
6
LittleShop/wwwroot/lib/fontawesome/css/all.min.css
vendored
Normal file
BIN
LittleShop/wwwroot/lib/fontawesome/webfonts/fa-brands-400.woff2
Normal file
BIN
LittleShop/wwwroot/lib/fontawesome/webfonts/fa-solid-900.woff2
Normal file
2
LittleShop/wwwroot/lib/jquery/jquery.min.js
vendored
Normal file
2580
LittleShop/wwwroot/lib/radzen/Radzen.Blazor.js
Normal file
24431
LittleShop/wwwroot/lib/radzen/dark-base.css
Normal file
24578
LittleShop/wwwroot/lib/radzen/dark.css
Normal file
90
LittleShop/wwwroot/manifest.json
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"name": "LittleShop Admin",
|
||||||
|
"short_name": "LittleShop",
|
||||||
|
"description": "Modern e-commerce admin panel for managing orders, products, and customers",
|
||||||
|
"start_url": "/Admin/Dashboard",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "any",
|
||||||
|
"theme_color": "#2563eb",
|
||||||
|
"background_color": "#f9fafb",
|
||||||
|
"scope": "/Admin/",
|
||||||
|
"categories": ["business", "productivity", "shopping"],
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-72x72.png",
|
||||||
|
"sizes": "72x72",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-96x96.png",
|
||||||
|
"sizes": "96x96",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-128x128.png",
|
||||||
|
"sizes": "128x128",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-144x144.png",
|
||||||
|
"sizes": "144x144",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-152x152.png",
|
||||||
|
"sizes": "152x152",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shortcuts": [
|
||||||
|
{
|
||||||
|
"name": "Orders",
|
||||||
|
"short_name": "Orders",
|
||||||
|
"description": "View and manage orders",
|
||||||
|
"url": "/Admin/Orders",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Products",
|
||||||
|
"short_name": "Products",
|
||||||
|
"description": "Manage product catalog",
|
||||||
|
"url": "/Admin/Products",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
106
LittleShop/wwwroot/sw.js
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// LittleShop Admin - Service Worker
|
||||||
|
// Provides offline functionality and app-like experience
|
||||||
|
|
||||||
|
const CACHE_NAME = 'littleshop-admin-v1';
|
||||||
|
const urlsToCache = [
|
||||||
|
'/Admin/Dashboard',
|
||||||
|
'/Admin/Orders',
|
||||||
|
'/Admin/Products',
|
||||||
|
'/Admin/Categories',
|
||||||
|
'/lib/bootstrap/css/bootstrap.min.css',
|
||||||
|
'/lib/fontawesome/css/all.min.css',
|
||||||
|
'/lib/bootstrap-icons/bootstrap-icons.css',
|
||||||
|
'/css/modern-admin.css',
|
||||||
|
'/lib/jquery/jquery.min.js',
|
||||||
|
'/lib/bootstrap/js/bootstrap.bundle.min.js',
|
||||||
|
'/js/modern-mobile.js',
|
||||||
|
'/lib/fontawesome/webfonts/fa-solid-900.woff2',
|
||||||
|
'/lib/fontawesome/webfonts/fa-brands-400.woff2'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Install event - cache resources
|
||||||
|
self.addEventListener('install', (event) => {
|
||||||
|
console.log('SW: Installing service worker');
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then((cache) => {
|
||||||
|
console.log('SW: Caching app shell');
|
||||||
|
return cache.addAll(urlsToCache);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Activate event - clean up old caches
|
||||||
|
self.addEventListener('activate', (event) => {
|
||||||
|
console.log('SW: Activating service worker');
|
||||||
|
event.waitUntil(
|
||||||
|
caches.keys().then((cacheNames) => {
|
||||||
|
return Promise.all(
|
||||||
|
cacheNames.map((cacheName) => {
|
||||||
|
if (cacheName !== CACHE_NAME) {
|
||||||
|
console.log('SW: Deleting old cache:', cacheName);
|
||||||
|
return caches.delete(cacheName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch event - serve from cache, fallback to network
|
||||||
|
self.addEventListener('fetch', (event) => {
|
||||||
|
// Only handle GET requests
|
||||||
|
if (event.request.method !== 'GET') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(event.request)
|
||||||
|
.then((response) => {
|
||||||
|
// Return cached version or fetch from network
|
||||||
|
return response || fetch(event.request).catch(() => {
|
||||||
|
// If both cache and network fail, show offline page for HTML requests
|
||||||
|
if (event.request.headers.get('accept').includes('text/html')) {
|
||||||
|
return new Response(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>LittleShop Admin - Offline</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: system-ui, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
background: #f9fafb;
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
.offline-icon { font-size: 4rem; margin-bottom: 1rem; color: #6b7280; }
|
||||||
|
h1 { color: #1f2937; margin-bottom: 0.5rem; }
|
||||||
|
p { color: #6b7280; margin-bottom: 2rem; }
|
||||||
|
.btn {
|
||||||
|
background: #2563eb;
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="offline-icon">📱</div>
|
||||||
|
<h1>You're offline</h1>
|
||||||
|
<p>Please check your internet connection and try again.</p>
|
||||||
|
<a href="/Admin/Dashboard" class="btn">Try Again</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`, {
|
||||||
|
headers: { 'Content-Type': 'text/html' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
86
create_pwa_icons.ps1
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# PowerShell script to generate PWA icons
|
||||||
|
# Creates PNG icons for LittleShop Admin PWA
|
||||||
|
|
||||||
|
Add-Type -AssemblyName System.Drawing
|
||||||
|
|
||||||
|
function Create-Icon {
|
||||||
|
param(
|
||||||
|
[int]$Size,
|
||||||
|
[string]$OutputPath
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create bitmap
|
||||||
|
$bitmap = New-Object System.Drawing.Bitmap($Size, $Size)
|
||||||
|
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
|
||||||
|
$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
||||||
|
$graphics.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::AntiAlias
|
||||||
|
|
||||||
|
# Create gradient brush
|
||||||
|
$startColor = [System.Drawing.Color]::FromArgb(37, 99, 235) # #2563eb
|
||||||
|
$endColor = [System.Drawing.Color]::FromArgb(124, 58, 237) # #7c3aed
|
||||||
|
$gradientBrush = New-Object System.Drawing.Drawing2D.LinearGradientBrush(
|
||||||
|
[System.Drawing.Point]::new(0, 0),
|
||||||
|
[System.Drawing.Point]::new($Size, $Size),
|
||||||
|
$startColor,
|
||||||
|
$endColor
|
||||||
|
)
|
||||||
|
|
||||||
|
# Draw rounded rectangle background
|
||||||
|
$cornerRadius = [int]($Size * 0.15)
|
||||||
|
$rect = New-Object System.Drawing.Rectangle(0, 0, $Size, $Size)
|
||||||
|
|
||||||
|
# Create rounded rectangle path
|
||||||
|
$path = New-Object System.Drawing.Drawing2D.GraphicsPath
|
||||||
|
$path.AddArc(0, 0, $cornerRadius * 2, $cornerRadius * 2, 180, 90)
|
||||||
|
$path.AddArc($Size - $cornerRadius * 2, 0, $cornerRadius * 2, $cornerRadius * 2, 270, 90)
|
||||||
|
$path.AddArc($Size - $cornerRadius * 2, $Size - $cornerRadius * 2, $cornerRadius * 2, $cornerRadius * 2, 0, 90)
|
||||||
|
$path.AddArc(0, $Size - $cornerRadius * 2, $cornerRadius * 2, $cornerRadius * 2, 90, 90)
|
||||||
|
$path.CloseFigure()
|
||||||
|
|
||||||
|
# Fill background
|
||||||
|
$graphics.FillPath($gradientBrush, $path)
|
||||||
|
|
||||||
|
# Draw text "LS"
|
||||||
|
$font = New-Object System.Drawing.Font("Segoe UI", ($Size * 0.3), [System.Drawing.FontStyle]::Bold)
|
||||||
|
$brush = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::White)
|
||||||
|
$format = New-Object System.Drawing.StringFormat
|
||||||
|
$format.Alignment = [System.Drawing.StringAlignment]::Center
|
||||||
|
$format.LineAlignment = [System.Drawing.StringAlignment]::Center
|
||||||
|
|
||||||
|
$textRect = New-Object System.Drawing.RectangleF(0, 0, $Size, $Size)
|
||||||
|
$graphics.DrawString("LS", $font, $brush, $textRect, $format)
|
||||||
|
|
||||||
|
# Save image
|
||||||
|
$bitmap.Save($OutputPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
$graphics.Dispose()
|
||||||
|
$bitmap.Dispose()
|
||||||
|
$gradientBrush.Dispose()
|
||||||
|
$font.Dispose()
|
||||||
|
$brush.Dispose()
|
||||||
|
$path.Dispose()
|
||||||
|
|
||||||
|
Write-Host "Created icon: $OutputPath ($Size x $Size)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create icons directory
|
||||||
|
$iconsDir = "C:\Production\Source\LittleShop\LittleShop\wwwroot\icons"
|
||||||
|
if (!(Test-Path $iconsDir)) {
|
||||||
|
New-Item -ItemType Directory -Path $iconsDir -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate all required icon sizes
|
||||||
|
$sizes = @(72, 96, 128, 144, 152, 192, 384, 512)
|
||||||
|
|
||||||
|
foreach ($size in $sizes) {
|
||||||
|
$outputPath = Join-Path $iconsDir "icon-${size}x${size}.png"
|
||||||
|
Create-Icon -Size $size -OutputPath $outputPath
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "🎉 All PWA icons created successfully!"
|
||||||
|
Write-Host "Icons saved to: $iconsDir"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Generated icons:"
|
||||||
|
Get-ChildItem $iconsDir -Name "icon-*.png" | ForEach-Object { Write-Host " ✅ $_" }
|
||||||