diff --git a/web/dashboard/messages/ar.json b/web/dashboard/messages/ar.json index 6a2022a..33cec9d 100644 --- a/web/dashboard/messages/ar.json +++ b/web/dashboard/messages/ar.json @@ -1200,6 +1200,7 @@ "shop": "المقهى والمتجر", "shopGeneral": "الملف والتكاملات", "shopAppearance": "المظهر والألوان", + "shopNotifications": "الإشعارات والصوت", "printer": "الطابعة", "printerSettings": "إعدادات الطابعة", "printTest": "صفحة اختبار الطباعة", @@ -1207,6 +1208,39 @@ "team": "الفريق والموظفون", "customRoles": "الأدوار المخصصة" }, + "notifPrefs": { + "soundSection": "الصوت", + "soundEnabled": "تشغيل صوت للإشعارات الجديدة", + "soundEnabledHint": "يصدر صوتًا عند وصول طلب جديد أو نداء نادل أو تنبيه.", + "soundChoice": "صوت الإشعار", + "preview": "معاينة", + "volume": "مستوى الصوت", + "soundClassic": "كلاسيكي", + "soundDing": "رنين", + "soundBell": "جرس", + "soundChime": "أجراس", + "soundMarimba": "ماريمبا", + "soundAlert": "تنبيه", + "desktopSection": "إشعارات سطح المكتب", + "desktopHint": "إظهار نافذة منبثقة على ويندوز/سطح المكتب حتى عندما تكون لوحة التحكم في تبويب آخر أو مصغّرة.", + "enableDesktop": "تفعيل إشعارات سطح المكتب", + "desktopEnabled": "نوافذ سطح المكتب", + "desktopEnabledHint": "تظهر فقط عندما لا يكون هذا التبويب نشطًا.", + "desktopGranted": "تم تفعيل إشعارات سطح المكتب", + "desktopDenied": "تم رفض الإذن من المتصفح", + "desktopBlocked": "الإشعارات محظورة لهذا الموقع. اسمح بها من إعدادات الموقع في المتصفح ثم أعد التحميل.", + "desktopUnsupported": "هذا المتصفح لا يدعم إشعارات سطح المكتب.", + "desktopFocusNote": "تظهر النافذة التجريبية فقط إذا انتقلت إلى نافذة أخرى أولًا.", + "sendTest": "إرسال إشعار تجريبي", + "testTitle": "ميزي", + "testBody": "هذا إشعار تجريبي.", + "testToast": "تم إرسال الإشعار التجريبي", + "inAppSection": "داخل التطبيق", + "tabBadge": "عدد غير المقروء على تبويب المتصفح", + "tabBadgeHint": "يعرض عدد الإشعارات غير المقروءة في عنوان التبويب والأيقونة المفضلة.", + "toast": "تنبيه داخل التطبيق", + "toastHint": "إظهار شريط صغير داخل لوحة التحكم للإشعارات الجديدة." + }, "customRoles": { "title": "الأدوار المخصصة", "subtitle": "حدّد أدواراً بصلاحيات مخصصة لموظفيك", diff --git a/web/dashboard/messages/en.json b/web/dashboard/messages/en.json index 68bdd95..4bc949f 100644 --- a/web/dashboard/messages/en.json +++ b/web/dashboard/messages/en.json @@ -1272,6 +1272,7 @@ "shop": "Shop & café", "shopGeneral": "Profile & integrations", "shopAppearance": "Appearance & colors", + "shopNotifications": "Notifications & sound", "printer": "Printer", "printerSettings": "Printer settings", "printTest": "Print test page", @@ -1279,6 +1280,39 @@ "team": "Team & Staff", "customRoles": "Custom Roles" }, + "notifPrefs": { + "soundSection": "Sound", + "soundEnabled": "Play a sound for new notifications", + "soundEnabledHint": "Chimes when a new order, waiter call, or alert arrives.", + "soundChoice": "Notification sound", + "preview": "Preview", + "volume": "Volume", + "soundClassic": "Classic", + "soundDing": "Ding", + "soundBell": "Bell", + "soundChime": "Chime", + "soundMarimba": "Marimba", + "soundAlert": "Alert", + "desktopSection": "Desktop notifications", + "desktopHint": "Show a Windows/desktop popup even when the dashboard is in another tab or minimized.", + "enableDesktop": "Enable desktop notifications", + "desktopEnabled": "Desktop popups", + "desktopEnabledHint": "Pop up only when this tab is not focused.", + "desktopGranted": "Desktop notifications enabled", + "desktopDenied": "Permission denied by the browser", + "desktopBlocked": "Notifications are blocked for this site. Allow them in your browser's site settings, then reload.", + "desktopUnsupported": "This browser does not support desktop notifications.", + "desktopFocusNote": "A test popup only appears if you switch to another window first.", + "sendTest": "Send a test notification", + "testTitle": "Meezi", + "testBody": "This is a test notification.", + "testToast": "Test sent", + "inAppSection": "In-app", + "tabBadge": "Unread count on the browser tab", + "tabBadgeHint": "Shows the number of unread notifications in the tab title and favicon.", + "toast": "In-app toast", + "toastHint": "Show a small banner inside the dashboard for new notifications." + }, "customRoles": { "title": "Custom Roles", "subtitle": "Define roles with tailored permissions for your staff", diff --git a/web/dashboard/messages/fa.json b/web/dashboard/messages/fa.json index e574af0..84723d0 100644 --- a/web/dashboard/messages/fa.json +++ b/web/dashboard/messages/fa.json @@ -1273,6 +1273,7 @@ "shop": "کافه و فروشگاه", "shopGeneral": "پروفایل و اتصالها", "shopAppearance": "ظاهر و رنگبندی", + "shopNotifications": "اعلانها و صدا", "printer": "پرینتر", "printerSettings": "تنظیمات پرینتر", "printTest": "صفحه تست چاپ", @@ -1280,6 +1281,39 @@ "team": "تیم و کارمندان", "customRoles": "نقشهای سفارشی" }, + "notifPrefs": { + "soundSection": "صدا", + "soundEnabled": "پخش صدا برای اعلانهای جدید", + "soundEnabledHint": "هنگام رسیدن سفارش جدید، درخواست میز یا هشدار، صدا پخش میشود.", + "soundChoice": "صدای اعلان", + "preview": "پیشنمایش", + "volume": "بلندی صدا", + "soundClassic": "کلاسیک", + "soundDing": "دینگ", + "soundBell": "زنگ", + "soundChime": "ناقوس", + "soundMarimba": "ماریمبا", + "soundAlert": "هشدار", + "desktopSection": "اعلانهای دسکتاپ", + "desktopHint": "نمایش پاپآپ ویندوز/دسکتاپ حتی وقتی داشبورد در تب دیگری باز است یا کوچک شده.", + "enableDesktop": "فعالسازی اعلانهای دسکتاپ", + "desktopEnabled": "پاپآپ دسکتاپ", + "desktopEnabledHint": "فقط وقتی این تب فعال نیست نمایش داده میشود.", + "desktopGranted": "اعلانهای دسکتاپ فعال شد", + "desktopDenied": "دسترسی توسط مرورگر رد شد", + "desktopBlocked": "اعلانها برای این سایت مسدود شدهاند. از تنظیمات سایت در مرورگر اجازه دهید و سپس صفحه را دوباره بارگذاری کنید.", + "desktopUnsupported": "این مرورگر از اعلانهای دسکتاپ پشتیبانی نمیکند.", + "desktopFocusNote": "پاپآپ آزمایشی فقط زمانی نمایش داده میشود که ابتدا به پنجره دیگری بروید.", + "sendTest": "ارسال اعلان آزمایشی", + "testTitle": "میزی", + "testBody": "این یک اعلان آزمایشی است.", + "testToast": "اعلان آزمایشی ارسال شد", + "inAppSection": "درونبرنامه", + "tabBadge": "شمارش خواندهنشده روی تب مرورگر", + "tabBadgeHint": "تعداد اعلانهای خواندهنشده را در عنوان تب و فاویکون نشان میدهد.", + "toast": "نوتیف درونبرنامه", + "toastHint": "نمایش یک بنر کوچک داخل داشبورد برای اعلانهای جدید." + }, "customRoles": { "title": "نقشهای سفارشی", "subtitle": "نقشهایی با دسترسی دلخواه برای کارمندان تعریف کنید", diff --git a/web/dashboard/src/components/settings/settings-notifications-panel.tsx b/web/dashboard/src/components/settings/settings-notifications-panel.tsx new file mode 100644 index 0000000..13daefe --- /dev/null +++ b/web/dashboard/src/components/settings/settings-notifications-panel.tsx @@ -0,0 +1,227 @@ +"use client"; + +import { Bell, Volume2 } from "lucide-react"; +import { useTranslations } from "next-intl"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { LabeledField } from "@/components/ui/labeled-field"; +import { notify } from "@/lib/notify"; +import { useNotifPrefs } from "@/lib/notifications/notification-prefs.store"; +import { SOUND_PRESETS, playSound, type SoundId } from "@/lib/notifications/sounds"; +import { + showDesktopNotification, + useNotificationPermission, +} from "@/lib/notifications/use-desktop-notifications"; + +function Toggle({ + id, + checked, + onChange, + label, + hint, +}: { + id: string; + checked: boolean; + onChange: (v: boolean) => void; + label: string; + hint?: string; +}) { + return ( + + ); +} + +export function SettingsNotificationsPanel() { + const t = useTranslations("settings.notifPrefs"); + const prefs = useNotifPrefs(); + const hasHydrated = useNotifPrefs((s) => s._hasHydrated); + const setPrefs = useNotifPrefs((s) => s.setPrefs); + const { permission, request, supported } = useNotificationPermission(); + + if (!hasHydrated) { + return
…
; + } + + const enableDesktop = async () => { + const p = await request(); + if (p === "granted") { + setPrefs({ desktopEnabled: true }); + notify.success(t("desktopGranted")); + } else if (p === "denied") { + setPrefs({ desktopEnabled: false }); + notify.error(t("desktopDenied")); + } + }; + + const desktopReady = supported && permission === "granted"; + + return ( +{t("desktopHint")}
+ + {!supported ? ( ++ {t("desktopUnsupported")} +
+ ) : permission === "denied" ? ( ++ {t("desktopBlocked")} +
+ ) : permission !== "granted" ? ( + + ) : ( +{t("desktopFocusNote")}
+ ) : null} +