From 7f9bbdc4f745959f76c826cab2b8950f1eec6455 Mon Sep 17 00:00:00 2001
From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com>
Date: Mon, 26 Jan 2026 00:46:27 -0500
Subject: [PATCH] up
---
index.php | 46 ++++++++++++++++++++++++++++++++++++++++++++++
manifest.json | 20 ++++++++++++++++++++
service-worker.js | 43 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 109 insertions(+)
create mode 100644 manifest.json
create mode 100644 service-worker.js
diff --git a/index.php b/index.php
index f17f908..7b0fff2 100644
--- a/index.php
+++ b/index.php
@@ -39,6 +39,11 @@ try {
+
+
+
+
+
@@ -97,6 +102,11 @@ try {
0
+
+
+
+
+
@@ -1237,6 +1247,42 @@ try {
openSidebar = function() { console.debug('[index] openSidebar()'); _openSidebar(); };
closeSidebar = function() { console.debug('[index] closeSidebar()'); _closeSidebar(); };
+ // PWA: register service worker and handle install prompt
+ let deferredPrompt = null;
+ const pwaBtn = document.getElementById('pwa-install-btn');
+ const pwaWrap = document.getElementById('pwa-install-wrap');
+
+ window.addEventListener('beforeinstallprompt', (e) => {
+ e.preventDefault();
+ deferredPrompt = e;
+ if (pwaBtn) {
+ pwaBtn.style.display = 'inline-block';
+ if (pwaWrap) pwaWrap.classList.remove('d-none');
+ }
+ });
+
+ if (pwaBtn) {
+ pwaBtn.addEventListener('click', async () => {
+ if (!deferredPrompt) return;
+ deferredPrompt.prompt();
+ const { outcome } = await deferredPrompt.userChoice;
+ console.debug('PWA install result:', outcome);
+ if (outcome === 'accepted') {
+ pwaBtn.style.display = 'none';
+ }
+ deferredPrompt = null;
+ });
+ }
+
+ window.addEventListener('appinstalled', () => {
+ console.debug('PWA installed');
+ if (pwaBtn) pwaBtn.style.display = 'none';
+ try { if (NotificationManager && NotificationManager.showToast) NotificationManager.showToast({ message: 'App instalada correctamente' }); } catch(e){}
+ });
+
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.register('/service-worker.js').then(reg => console.debug('SW registered', reg)).catch(e => console.warn('SW registration failed', e));
+ }
// Close sidebar when a nav item is selected (mobile UX improvement)
document.querySelectorAll('.nav-link[data-tab]').forEach(link => {
link.addEventListener('click', () => {
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..664bd0a
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,20 @@
+{
+ "name": "WhatsApp Bot Manager",
+ "short_name": "WA Bot",
+ "start_url": "/index.php",
+ "display": "standalone",
+ "background_color": "#ffffff",
+ "theme_color": "#10b981",
+ "icons": [
+ {
+ "src": "data:image/svg+xml;utf8,",
+ "sizes": "192x192",
+ "type": "image/svg+xml"
+ },
+ {
+ "src": "data:image/svg+xml;utf8,",
+ "sizes": "512x512",
+ "type": "image/svg+xml"
+ }
+ ]
+}
diff --git a/service-worker.js b/service-worker.js
new file mode 100644
index 0000000..67c97ea
--- /dev/null
+++ b/service-worker.js
@@ -0,0 +1,43 @@
+const CACHE_NAME = 'whatsapp-static-v1';
+const OFFLINE_URL = '/index.php';
+const ASSETS_TO_CACHE = [
+ OFFLINE_URL,
+ '/assets/css/styles.css',
+ '/assets/js/app_simple.js',
+ '/manifest.json'
+];
+
+self.addEventListener('install', (event) => {
+ self.skipWaiting();
+ event.waitUntil(
+ caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS_TO_CACHE))
+ );
+});
+
+self.addEventListener('activate', (event) => {
+ event.waitUntil(
+ caches.keys().then(keys => Promise.all(
+ keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))
+ ))
+ );
+ self.clients.claim();
+});
+
+self.addEventListener('fetch', (event) => {
+ if (event.request.method !== 'GET') return;
+ event.respondWith(
+ caches.match(event.request).then(cached => {
+ if (cached) return cached;
+ return fetch(event.request).then(resp => {
+ // cache same-origin GET responses
+ try {
+ if (resp && resp.status === 200 && resp.type === 'basic') {
+ const clone = resp.clone();
+ caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
+ }
+ } catch (e) {}
+ return resp;
+ }).catch(() => caches.match(OFFLINE_URL));
+ })
+ );
+});