diff --git a/Layout/default/VueViews/WorkorderCompanyPWA.php b/Layout/default/VueViews/WorkorderCompanyPWA.php new file mode 100644 index 000000000..e545ec327 --- /dev/null +++ b/Layout/default/VueViews/WorkorderCompanyPWA.php @@ -0,0 +1,541 @@ + + + + + + + Workorders + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + diff --git a/application/WorkorderCompany/WorkorderCompanyController.php b/application/WorkorderCompany/WorkorderCompanyController.php index 646f0909c..9ecc8aada 100644 --- a/application/WorkorderCompany/WorkorderCompanyController.php +++ b/application/WorkorderCompany/WorkorderCompanyController.php @@ -30,6 +30,20 @@ class WorkorderCompanyController extends WorkorderBaseController { parent::indexAction(); } + public function mobileAction() { + $company = WorkorderCompanyModel::getFirst(['addressId' => $this->user->address_id]); + + $vue_config = [ + 'BASE_PATH' => '/WorkorderCompany', + 'COMPANY_ID' => $company ? $company->id : 0, + // You can add more global variables for your Vue app here + ]; + + // This tells the framework to use your new PWA layout file + $this->layout()->setTemplate("VueViews/WorkorderCompanyPWA"); + $this->layout()->set("JSGlobals", $vue_config); + } + protected function getAction() { $pagination = $this->postData['pagination'] ?? ['page' => 1, 'per_page' => 10]; $filters = $this->postData['filters'] ?? []; diff --git a/public/js/pages/WorkorderBase/WorkorderServiceWorker.js b/public/js/pages/WorkorderBase/WorkorderServiceWorker.js new file mode 100644 index 000000000..4c1df3860 --- /dev/null +++ b/public/js/pages/WorkorderBase/WorkorderServiceWorker.js @@ -0,0 +1,73 @@ +const CACHE_NAME = 'workorder-company-cache-v1'; +const URLS_TO_CACHE = [ + // We don't cache the root URL here as the scope is /WorkorderCompany/ + '/WorkorderCompany/mobile', // The main entry point + // Add other assets like icons if you have them +]; + +// Install the service worker and cache essential assets +self.addEventListener('install', event => { + event.waitUntil( + caches.open(CACHE_NAME) + .then(cache => { + console.log('Opened cache'); + return cache.addAll(URLS_TO_CACHE); + }) + ); +}); + +// Serve cached content when offline +self.addEventListener('fetch', event => { + // Only handle requests within the specified scope + if (event.request.url.includes('/WorkorderCompany/')) { + event.respondWith( + caches.match(event.request) + .then(response => { + // Cache hit - return response + if (response) { + return response; + } + + // Not in cache, fetch from network + return fetch(event.request).then( + function(response) { + // Check if we received a valid response + if(!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + + // IMPORTANT: Clone the response. A response is a stream + // and because we want the browser to consume the response + // as well as the cache consuming the response, we need + // to clone it so we have two streams. + var responseToCache = response.clone(); + + caches.open(CACHE_NAME) + .then(function(cache) { + cache.put(event.request, responseToCache); + }); + + return response; + } + ); + }) + ); + } + // For other requests, do nothing and let the browser handle it. +}); + +// Update the cache with new assets +self.addEventListener('activate', event => { + const cacheWhitelist = [CACHE_NAME]; + event.waitUntil( + caches.keys().then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheWhitelist.indexOf(cacheName) === -1) { + return caches.delete(cacheName); + } + }) + ); + }) + ); +}); diff --git a/public/js/pages/WorkorderBase/manifest.json b/public/js/pages/WorkorderBase/manifest.json new file mode 100644 index 000000000..63ee4ec3d --- /dev/null +++ b/public/js/pages/WorkorderBase/manifest.json @@ -0,0 +1,24 @@ +{ + "name": "Workorder Company PWA", + "short_name": "Workorders", + "description": "A PWA for managing workorders efficiently on mobile devices.", + "start_url": "/WorkorderCompany/mobile", + "display": "standalone", + "background_color": "#f1f5f9", + "theme_color": "#4f46e5", + "orientation": "portrait-primary", + "icons": [ + { + "src": "https://placehold.co/192x192/4f46e5/ffffff?text=WO", + "type": "image/png", + "sizes": "192x192", + "purpose": "any maskable" + }, + { + "src": "https://placehold.co/512x512/4f46e5/ffffff?text=WO", + "type": "image/png", + "sizes": "512x512", + "purpose": "any maskable" + } + ] +}