diff --git a/src/components/UI/PwaInstallButton.jsx b/src/components/UI/PwaInstallButton.jsx
new file mode 100644
index 0000000..319cdcb
--- /dev/null
+++ b/src/components/UI/PwaInstallButton.jsx
@@ -0,0 +1,76 @@
+import React from "react";
+
+/**
+ * Compact PWA install button for the header.
+ * - Shows π² icon when install prompt is available (Chrome/Edge on Android & desktop).
+ * - Shows iOS Safari instructions tooltip on click when on iOS.
+ * - Hidden when app is already installed (standalone mode).
+ * - After install: auto-hidden via appinstalled event.
+ */
+export const PwaInstallButton = ({ onInstall, isInstalled, isInstallAvailable }) => {
+ const [showTip, setShowTip] = React.useState(false);
+ const tipRef = React.useRef(null);
+
+ // Detect iOS (no beforeinstallprompt, but can be added to home screen)
+ const isIOS = typeof navigator !== "undefined" && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
+
+ const handleInstallClick = async () => {
+ if (isInstallAvailable && onInstall) {
+ await onInstall();
+ } else {
+ // Show instruction tip for iOS or unsupported browsers
+ setShowTip((prev) => !prev);
+ }
+ };
+
+ // Close tip on outside click
+ React.useEffect(() => {
+ if (!showTip) return;
+ const handler = (e) => {
+ if (tipRef.current && !tipRef.current.contains(e.target)) {
+ setShowTip(false);
+ }
+ };
+ document.addEventListener("click", handler);
+ return () => document.removeEventListener("click", handler);
+ }, [showTip]);
+
+ // Don't render if already installed as PWA β AFTER all hooks
+ if (isInstalled) return null;
+
+ return (
+
+
+
+ {showTip && (
+
+ {isIOS ? (
+ <>
+
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° Π½Π° iOS
+
+ - ΠΡΠΊΡΠΎΠΉΡΠ΅ Π² Safari
+ - ΠΠ°ΠΆΠΌΠΈΡΠ΅ ΠΠΎΠ΄Π΅Π»ΠΈΡΡΡΡ β¬οΈ
+ - ΠΡΠ±Π΅ΡΠΈΡΠ΅ Β«ΠΠ° ΡΠΊΡΠ°Π½ ΠΠΎΠΌΠΎΠΉΒ»
+
+ >
+ ) : (
+ <>
+
Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ°
+
+ ΠΠ°ΠΆΠΌΠΈΡΠ΅ Π·Π½Π°ΡΠΎΠΊ π² Π² Π°Π΄ΡΠ΅ΡΠ½ΠΎΠΉ ΡΡΡΠΎΠΊΠ΅ Π±ΡΠ°ΡΠ·Π΅ΡΠ° ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΌΠ΅Π½Ρ Β«Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Β».
+
+ >
+ )}
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/src/layouts/AppShell.jsx b/src/layouts/AppShell.jsx
index d4d5d29..ae1fbd0 100644
--- a/src/layouts/AppShell.jsx
+++ b/src/layouts/AppShell.jsx
@@ -4,11 +4,15 @@ import { Badge } from "../components/UI/Badge";
import { Button } from "../components/UI/Button";
import { Panel } from "../components/UI/Panel";
import { ThemeToggle } from "../components/UI/ThemeToggle";
+import { PwaInstallButton } from "../components/UI/PwaInstallButton";
import { NotificationBell } from "../components/notifications/NotificationBell";
import { NotificationSettings } from "../components/notifications/NotificationSettings";
export const AppShell = ({
user,
+ onInstallApp,
+ isInstalled,
+ isInstallAvailable,
onSignOut,
onOpenGuide,
isGuideOpen = false,
@@ -108,6 +112,7 @@ export const AppShell = ({
{isGuideOpen ? "ΠΠ°Π·Π°Π΄" : "?"}
) : null}
+
) : null}
+
diff --git a/src/pages/DashboardPage.jsx b/src/pages/DashboardPage.jsx
index cd8948f..81c15e8 100644
--- a/src/pages/DashboardPage.jsx
+++ b/src/pages/DashboardPage.jsx
@@ -8,6 +8,7 @@ import { ProductGuidePanel } from "../components/dashboard/ProductGuidePanel";
import { useAuth } from "../context/AuthContext";
import { useNotifications } from "../hooks/useNotifications";
import { usePushNotifications } from "../hooks/usePushNotifications";
+import { usePwaStatus } from "../hooks/usePwaStatus";
import { useOrderGroups } from "../hooks/useOrderGroups";
import { AppShell } from "../layouts/AppShell";
@@ -53,6 +54,8 @@ export const DashboardPage = () => {
}
}, [isSupported, isSubscribed, user?.id, subscribe]);
+ const { isInstalled, isInstallAvailable, installApp: onInstallApp } = usePwaStatus();
+
const {
orderGroups,
allOrderGroups,
@@ -143,6 +146,9 @@ export const DashboardPage = () => {
setActiveSection((current) => (current === "guide" ? section.key : "guide"))}
isGuideOpen={isGuideOpen}
navItems={navItems}