feat: add PWA support with manifest configuration and offline connectivity indicator

This commit is contained in:
poyrazavsever
2026-06-06 21:53:06 +03:00
parent 83379a5edc
commit 4a52deec4d
6 changed files with 2221 additions and 1 deletions
+9
View File
@@ -3,12 +3,20 @@ import "./globals.css";
import { Geist } from "next/font/google";
import { cn } from "@/lib/utils";
import { Toaster } from "react-hot-toast";
import { OfflineIndicator } from "@/components/ui/offline-indicator";
const geist = Geist({ subsets: ["latin"], variable: "--font-sans" });
export const metadata: Metadata = {
title: "Cognis",
description: "Self-hosted freelancer operating dashboard",
manifest: "/manifest.json",
themeColor: "#ffffff",
appleWebApp: {
capable: true,
statusBarStyle: "default",
title: "Cognis",
},
};
export default function RootLayout({
@@ -24,6 +32,7 @@ export default function RootLayout({
>
<body>
{children}
<OfflineIndicator />
<Toaster position="top-right" />
</body>
</html>
+33
View File
@@ -0,0 +1,33 @@
"use client";
import { useEffect, useState } from "react";
import { WifiOff } from "lucide-react";
export function OfflineIndicator() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// Sadece client-side'da çalışır
setIsOnline(navigator.onLine);
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}, []);
if (isOnline) return null;
return (
<div className="fixed bottom-4 right-4 z-50 flex items-center gap-2 rounded-lg bg-destructive px-4 py-2 text-sm font-medium text-destructive-foreground shadow-lg animate-in fade-in slide-in-from-bottom-4">
<WifiOff className="h-4 w-4" />
Çevrimdışı moddasınız. Değişiklikler senkronize edilmeyecek.
</div>
);
}
+9 -1
View File
@@ -1,4 +1,12 @@
import type { NextConfig } from "next";
import withPWAInit from "@ducanh2912/next-pwa";
const withPWA = withPWAInit({
dest: "public",
disable: process.env.NODE_ENV === "development",
register: true,
skipWaiting: true,
});
const nextConfig: NextConfig = {
output: "standalone",
@@ -9,4 +17,4 @@ const nextConfig: NextConfig = {
},
};
export default nextConfig;
export default withPWA(nextConfig);
+1
View File
@@ -13,6 +13,7 @@
"@ai-sdk/openai": "^3.0.68",
"@ai-sdk/react": "^3.0.199",
"@base-ui/react": "^1.5.0",
"@ducanh2912/next-pwa": "^10.2.9",
"@hookform/resolvers": "^5.4.0",
"@iconify/react": "^6.0.2",
"@radix-ui/react-alert-dialog": "^1.1.15",
+2146
View File
File diff suppressed because it is too large Load Diff
+23
View File
@@ -0,0 +1,23 @@
{
"name": "Cognis",
"short_name": "Cognis",
"description": "Self-hosted freelancer operating dashboard",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"orientation": "portrait",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "/logo/LogoWithBg.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/logo/LogoWithBg.png",
"sizes": "512x512",
"type": "image/png"
}
]
}