diff --git a/public/icons/icon.svg b/public/icons/icon.svg new file mode 100644 index 0000000..182daef --- /dev/null +++ b/public/icons/icon.svg @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..502d693 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,22 @@ +{ + "name": "WFDASH", + "short_name": "WFDASH", + "description": "Business CRM — Quotes, Orders, Customers", + "start_url": "/", + "display": "standalone", + "background_color": "#FDF6F5", + "theme_color": "#E85D4C", + "orientation": "any", + "icons": [ + { + "src": "/icons/icon.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any maskable" + } + ], + "categories": ["business", "productivity"], + "screenshots": [], + "related_applications": [], + "prefer_related_applications": false +} \ No newline at end of file diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..f8605b9 --- /dev/null +++ b/public/sw.js @@ -0,0 +1,53 @@ +const CACHE_NAME = "wfdash-v1"; +const OFFLINE_URL = "/offline"; + +const PRECACHE_ASSETS = [ + "/", + "/offline", + "/manifest.json", +]; + +self.addEventListener("install", (event) => { + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => { + return cache.addAll(PRECACHE_ASSETS); + }) + ); + self.skipWaiting(); +}); + +self.addEventListener("activate", (event) => { + event.waitUntil( + caches.keys().then((keys) => { + return Promise.all( + keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)) + ); + }) + ); + self.clients.claim(); +}); + +self.addEventListener("fetch", (event) => { + if (event.request.method !== "GET") return; + if (event.request.url.includes("/api/")) return; + + event.respondWith( + fetch(event.request) + .then((response) => { + const clone = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, clone); + }); + return response; + }) + .catch(() => { + return caches.match(event.request).then((cached) => { + if (cached) return cached; + if (event.request.mode === "navigate") { + return caches.match(OFFLINE_URL); + } + return new Response("Offline", { status: 503 }); + }); + }) + ); +}); \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx index dd983ce..f8b85f6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,10 +1,11 @@ -import type { Metadata } from "next"; +import type { Metadata, Viewport } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { ThemeProvider } from "@/components/theme-provider"; import { AuthProvider } from "@/components/auth-provider"; import { Toaster } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; +import { ServiceWorkerRegistration } from "@/components/service-worker-registration"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -16,9 +17,26 @@ const geistMono = Geist_Mono({ subsets: ["latin"], }); +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, + viewportFit: "cover", + themeColor: "#E85D4C", +}; + export const metadata: Metadata = { title: "WFDASH - Business CRM", description: "Business CRM for managing customers, companies, suppliers, quotes, and orders", + manifest: "/manifest.json", + appleWebApp: { + capable: true, + statusBarStyle: "default", + title: "WFDASH", + }, + icons: { + icon: "/icons/icon.svg", + apple: "/icons/icon.svg", + }, }; export default function RootLayout({ children }: { children: React.ReactNode }) { @@ -29,6 +47,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) suppressHydrationWarning >
++ Looks like you have lost your internet connection. Please check your connection and try again. +
+ + + +