From 1686efeeade7d9a6054f4f0e4be99a31c0bd023b Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Fri, 13 Mar 2026 17:35:06 +0200 Subject: [PATCH] feat(frontend): PWA + Backup/Restore + Upgrade Prompts + Settings - PWA: vite-plugin-pwa with autoUpdate service worker, manifest, offline caching for assets + NetworkFirst for API calls - PWA icons: 192x192 and 512x512 placeholder PNGs + favicon.svg - index.html: theme-color, apple-touch-icon, description meta tags - UpgradeBanner component: trial expiry warning with upgrade CTA - SettingsView: complete settings page with: - Plan info display - Tenant profile form (firma, CUI, reg com, adresa, IBAN, banca) - Backup export (JSON with all tenant data from wa-sqlite) - Restore import (JSON file with validation and INSERT OR REPLACE) - User management with invite form (email + rol) - Logout button - useOffline composable: shared reactive online/offline state - DashboardView: added UpgradeBanner at top Co-Authored-By: Claude Sonnet 4.6 --- frontend/index.html | 3 + frontend/public/favicon.svg | 5 + frontend/public/icon-192.png | Bin 0 -> 547 bytes frontend/public/icon-512.png | Bin 0 -> 1881 bytes .../src/components/common/UpgradeBanner.vue | 54 ++++ frontend/src/composables/useOffline.js | 21 ++ .../src/views/dashboard/DashboardView.vue | 3 + frontend/src/views/settings/SettingsView.vue | 283 +++++++++++++++++- frontend/vite.config.js | 34 +++ 9 files changed, 402 insertions(+), 1 deletion(-) create mode 100644 frontend/public/favicon.svg create mode 100644 frontend/public/icon-192.png create mode 100644 frontend/public/icon-512.png create mode 100644 frontend/src/components/common/UpgradeBanner.vue create mode 100644 frontend/src/composables/useOffline.js diff --git a/frontend/index.html b/frontend/index.html index 0b68506..6fffe1f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,6 +3,9 @@ + + + ROA AUTO diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..e2b0333 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,5 @@ + + + ROA + AUTO + diff --git a/frontend/public/icon-192.png b/frontend/public/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..5a1d5492bd315734c1818f721907ad4efae7e1e6 GIT binary patch literal 547 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvE2}s`E_d9@rf$^26i(^Q|oVS+@c^MQK4jAZs zc*yXd@8bbJr%Owi%5u(ZeR!)P?!a{(mm~!t=7|y=M;uhxlnezr8$CvahE_;eep|@2 VWRA0&2Qcy(JYD@<);T3K0RZc}ms9`% literal 0 HcmV?d00001 diff --git a/frontend/public/icon-512.png b/frontend/public/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..ff6999fad5e9de0c379cc013b81a511e55bec296 GIT binary patch literal 1881 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&t&wwUqN(1_pKoPZ!6KiaBqu8uBt2@Eq6> zbi +
+
+
+

+ {{ isExpired ? 'Perioada de trial a expirat' : 'Trial activ' }} +

+

+ + +

+
+ + Upgradeaza + +
+
+ + + diff --git a/frontend/src/composables/useOffline.js b/frontend/src/composables/useOffline.js new file mode 100644 index 0000000..87c8a9b --- /dev/null +++ b/frontend/src/composables/useOffline.js @@ -0,0 +1,21 @@ +import { ref, onMounted, onUnmounted } from 'vue' + +const online = ref(navigator.onLine) + +function setOnline() { online.value = true } +function setOffline() { online.value = false } + +let listenersAttached = false + +export function useOffline() { + onMounted(() => { + if (!listenersAttached) { + window.addEventListener('online', setOnline) + window.addEventListener('offline', setOffline) + listenersAttached = true + } + online.value = navigator.onLine + }) + + return { online } +} diff --git a/frontend/src/views/dashboard/DashboardView.vue b/frontend/src/views/dashboard/DashboardView.vue index 2c3d2ca..0ccb3cd 100644 --- a/frontend/src/views/dashboard/DashboardView.vue +++ b/frontend/src/views/dashboard/DashboardView.vue @@ -2,6 +2,8 @@

Dashboard

+ +
@@ -70,6 +72,7 @@ import { ref, onMounted } from 'vue' import { useOrdersStore } from '../../stores/orders.js' import { useSync } from '../../composables/useSync.js' +import UpgradeBanner from '../../components/common/UpgradeBanner.vue' useSync() diff --git a/frontend/src/views/settings/SettingsView.vue b/frontend/src/views/settings/SettingsView.vue index 1667820..c072f57 100644 --- a/frontend/src/views/settings/SettingsView.vue +++ b/frontend/src/views/settings/SettingsView.vue @@ -1,6 +1,287 @@ + + diff --git a/frontend/vite.config.js b/frontend/vite.config.js index f6821ef..638f08e 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,11 +1,45 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import tailwindcss from '@tailwindcss/vite' +import { VitePWA } from 'vite-plugin-pwa' export default defineConfig({ plugins: [ vue(), tailwindcss(), + VitePWA({ + registerType: 'autoUpdate', + workbox: { + globPatterns: ['**/*.{js,css,html,svg,wasm}'], + maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, + runtimeCaching: [ + { + urlPattern: /^https?:\/\/.*\/api\//, + handler: 'NetworkFirst', + options: { + cacheName: 'api-cache', + expiration: { maxEntries: 50, maxAgeSeconds: 300 }, + }, + }, + ], + }, + manifest: { + name: 'ROA AUTO - Management Service Auto', + short_name: 'ROA AUTO', + description: 'Aplicatie de management pentru service-uri auto', + theme_color: '#111827', + background_color: '#f9fafb', + display: 'standalone', + orientation: 'portrait-primary', + start_url: '/', + scope: '/', + icons: [ + { src: '/icon-192.png', sizes: '192x192', type: 'image/png' }, + { src: '/icon-512.png', sizes: '512x512', type: 'image/png' }, + { src: '/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' }, + ], + }, + }), ], server: { proxy: {