diff --git a/frontend/assets/icon-192.png b/frontend/assets/icon-192.png index a2bf7ae..9177cb5 100644 Binary files a/frontend/assets/icon-192.png and b/frontend/assets/icon-192.png differ diff --git a/frontend/assets/icon-512.png b/frontend/assets/icon-512.png index b0fb81f..9177cb5 100644 Binary files a/frontend/assets/icon-512.png and b/frontend/assets/icon-512.png differ diff --git a/frontend/css/main.css b/frontend/css/main.css index e81b6c9..4d078f4 100644 --- a/frontend/css/main.css +++ b/frontend/css/main.css @@ -2,8 +2,17 @@ body { display: flex; overflow: hidden; + font-family: 'Michroma', sans-serif; } +h1, h2, h3, h4, h5, h6, .logo { + font-family: 'Goldman', sans-serif; +} + +/* Responsiveness Utility Classes */ +@media (max-width: 767px) { .show-on-desktop { display: none !important; } } +@media (min-width: 768px) { .show-on-mobile { display: none !important; } } + .sidebar { width: var(--sidebar-width); height: 100vh; @@ -305,6 +314,17 @@ body { align-items: center; justify-content: space-between; margin-bottom: 24px; + position: sticky; + top: 0; + z-index: 50; + background: rgba(17, 17, 19, 0.8); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + padding: 16px 32px; + margin-left: -32px; + margin-right: -32px; + margin-top: -24px; + border-bottom: 1px solid var(--border); } .page-header h1 { @@ -321,7 +341,7 @@ body { /* Buttons */ .btn { padding: 8px 16px; - border-radius: var(--radius); + border-radius: var(--radius-xl); font-weight: 500; font-size: 13px; transition: all var(--transition); @@ -329,6 +349,9 @@ body { align-items: center; gap: 6px; } +.btn:active { + transform: scale(0.98); +} .btn-primary { background: var(--accent); diff --git a/frontend/css/variables.css b/frontend/css/variables.css index cdd4c1a..2447a60 100644 --- a/frontend/css/variables.css +++ b/frontend/css/variables.css @@ -1,27 +1,45 @@ :root { - --bg-primary: #111827; - --bg-secondary: #1f2937; - --bg-card: #1e293b; - --bg-card-hover: #283548; - --bg-input: #0f172a; - --accent: #3b82f6; - --accent-hover: #2563eb; - --accent-dim: #1d4ed8; - --text-primary: #f1f5f9; - --text-secondary: #94a3b8; - --text-muted: #64748b; - --success: #22c55e; - --success-dim: #15803d; + /* Cores de Destaque */ + --accent: #ff6b00; + --accent-hover: #e65c00; + --accent-dim: rgba(255, 107, 0, 0.15); + --accent-glow: rgba(255, 107, 0, 0.35); + + /* Sistema de Feedback */ + --success: #34d399; --danger: #ef4444; - --danger-dim: #991b1b; - --warning: #f59e0b; - --warning-dim: #92400e; - --border: #334155; - --border-light: #475569; - --radius: 8px; - --radius-lg: 12px; - --shadow: 0 4px 24px rgba(0, 0, 0, 0.4); - --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.3); + --warning: #fbbf24; + + /* Borda arredondada / Glassmorphism */ + --radius-sm: 8px; + --radius-md: 14px; + --radius-lg: 20px; + --radius-xl: 28px; + + --transition: 0.22s cubic-bezier(0.4, 0, 0.2, 1); --sidebar-width: 220px; - --transition: 0.2s ease; +} + +[data-theme="dark"], :root { + --bg-base: #09090b; + --bg-surface: #111113; + --bg-card: #1a1a1e; + --bg-card-hover: #242429; + --bg-input: #0d0d10; + --text-primary: #ffffff; + --text-secondary: #a1a1aa; + --text-muted: #6b7280; + --border: #27272a; + --border-highlight: #3f3f46; + --shadow: rgba(0, 0, 0, 0.5); + + /* Mapping old variables to new ones for compatibility */ + --bg-primary: var(--bg-base); + --bg-secondary: var(--bg-surface); + --radius: var(--radius-sm); + --success-dim: rgba(52, 211, 153, 0.2); + --danger-dim: rgba(239, 68, 68, 0.2); + --warning-dim: rgba(251, 191, 36, 0.2); + --border-light: var(--border-highlight); + --shadow-sm: rgba(0, 0, 0, 0.3); } diff --git a/frontend/index.html b/frontend/index.html index 44c36d0..3fb8e0f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,16 +1,21 @@ - + - - + + - ScreenTinker + GoldenSignage + + + + + @@ -30,7 +35,7 @@ - ScreenTinker + GoldenSignage diff --git a/frontend/js/app.js b/frontend/js/app.js index f155577..3c02640 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -543,7 +543,7 @@ window.addEventListener('hashchange', () => { if (isAuthenticated()) refreshCurr // Register PWA service worker if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('/sw-admin.js').catch(() => {}); + navigator.serviceWorker.register('/sw.js').catch(() => {}); } // Mobile sidebar: open/close via hamburger, backdrop, nav tap, Escape @@ -640,3 +640,45 @@ document.addEventListener('click', (e) => { const modal = document.getElementById(id); if (modal) modal.style.display = 'none'; }); + +// Custom Pull-to-Refresh logic for mobile PWA standalone +let pwaTouchStartY = 0; +let pwaIsPulling = false; + +document.addEventListener('touchstart', (e) => { + // Only start pull-to-refresh if we are at the very top of the page + const scrollElement = document.querySelector('.content') || document.documentElement; + if (scrollElement.scrollTop === 0) { + pwaTouchStartY = e.changedTouches[0].screenY; + pwaIsPulling = true; + } else { + pwaIsPulling = false; + } +}, { passive: true }); + +document.addEventListener('touchend', (e) => { + if (!pwaIsPulling) return; + const pwaTouchEndY = e.changedTouches[0].screenY; + + if (pwaTouchEndY - pwaTouchStartY > 150) { + if ('caches' in window) { + caches.keys().then((names) => { + Promise.all(names.map(name => caches.delete(name))) + .then(() => { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations().then((registrations) => { + for (let registration of registrations) { + registration.unregister(); + } + window.location.reload(true); + }); + } else { + window.location.reload(true); + } + }); + }); + } else { + window.location.reload(true); + } + } +}, { passive: true }); diff --git a/frontend/manifest.json b/frontend/manifest.json index ad2b6ec..811093d 100644 --- a/frontend/manifest.json +++ b/frontend/manifest.json @@ -1,11 +1,11 @@ { - "name": "ScreenTinker", - "short_name": "ScreenTinker", + "name": "GoldenSignage", + "short_name": "GoldenSignage", "description": "Digital Signage Management", "start_url": "/", "display": "standalone", - "background_color": "#111827", - "theme_color": "#3B82F6", + "background_color": "#09090b", + "theme_color": "#ff6b00", "orientation": "any", "icons": [ { diff --git a/frontend/sw.js b/frontend/sw.js new file mode 100644 index 0000000..f59d884 --- /dev/null +++ b/frontend/sw.js @@ -0,0 +1,54 @@ +const CACHE_NAME = 'goldensignage-v1'; +const ASSETS_TO_CACHE = [ + '/', + '/index.html', + '/manifest.json', + '/css/reset.css', + '/css/variables.css', + '/css/main.css', + '/js/app.js', + '/assets/icon-192.png', + '/assets/icon-512.png', + 'https://fonts.googleapis.com/css2?family=Goldman:wght@400;700&family=Michroma&display=swap' +]; + +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => { + return cache.addAll(ASSETS_TO_CACHE); + }) + ); + self.skipWaiting(); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.map((cacheName) => { + if (cacheName !== CACHE_NAME) { + return caches.delete(cacheName); + } + }) + ); + }) + ); + self.clients.claim(); +}); + +self.addEventListener('fetch', (event) => { + // Cache-First Strategy + event.respondWith( + caches.match(event.request).then((cachedResponse) => { + if (cachedResponse) { + return cachedResponse; + } + return fetch(event.request).then((networkResponse) => { + // Cache dynamic assets if needed, but for now just return network response + return networkResponse; + }).catch(() => { + // Handle offline fallback if needed + }); + }) + ); +});