From 1de110818ae4623cba13f78df7b64cc30213a3d3 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:08:29 +0700 Subject: [PATCH] feat: implement notification dropdown component and add localized notification strings --- .../layouts/components/header.layout.tsx | 6 +- .../notifications/notification-dropdown.tsx | 148 ++++++++++++++++++ packages/core-i18n/src/locales/en/common.json | 10 +- packages/core-i18n/src/locales/id/common.json | 10 +- 4 files changed, 169 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/apps/modules/layouts/components/notifications/notification-dropdown.tsx diff --git a/apps/web/src/apps/modules/layouts/components/header.layout.tsx b/apps/web/src/apps/modules/layouts/components/header.layout.tsx index 8920a2f..d50bc56 100644 --- a/apps/web/src/apps/modules/layouts/components/header.layout.tsx +++ b/apps/web/src/apps/modules/layouts/components/header.layout.tsx @@ -6,7 +6,6 @@ import { Text, useCoreAppShell, ActionIcon, - Indicator, Menu, Avatar, UnstyledButton, @@ -14,6 +13,7 @@ import { } from '@repo/ui/components'; import { Globe, Bell, HelpCircle, Settings, User, Briefcase, LogOut, ChevronDown } from 'lucide-react'; import { AppStorageKey, secureStorage } from '../../../../core/storage/local'; +import { NotificationDropdown } from './notifications/notification-dropdown'; // Dummy user data for preview const USER = { @@ -60,11 +60,11 @@ export default function HeaderLayout() { - + - + diff --git a/apps/web/src/apps/modules/layouts/components/notifications/notification-dropdown.tsx b/apps/web/src/apps/modules/layouts/components/notifications/notification-dropdown.tsx new file mode 100644 index 0000000..087e7bf --- /dev/null +++ b/apps/web/src/apps/modules/layouts/components/notifications/notification-dropdown.tsx @@ -0,0 +1,148 @@ +import { useState } from 'react'; +import { useTranslation } from '@repo/core-i18n'; +import { + Popover, + Group, + Text, + UnstyledButton, + ScrollArea, + Box, + Indicator, + Button, + Center, + Stack, + NavLink, +} from '@repo/ui/components'; +import { BellOff } from 'lucide-react'; + +interface NotificationItem { + id: string; + title: string; + description: string; + timestamp: string; + isRead: boolean; +} + +interface NotificationDropdownProps { + children: React.ReactNode; +} + +export function NotificationDropdown({ children }: NotificationDropdownProps) { + const { t } = useTranslation(); + const [opened, setOpened] = useState(false); + + const [notifications, setNotifications] = useState([ + { + id: '1', + title: 'New User Registration', + description: 'John Doe has registered a new account in the system.', + timestamp: t('common:notifications_justNow'), + isRead: false, + }, + { + id: '2', + title: 'System Update Completed', + description: 'The ERP database has been successfully updated to v2.4.', + timestamp: `2 ${t('common:notifications_hoursAgo')}`, + isRead: false, + }, + { + id: '3', + title: 'Weekly Report Ready', + description: 'Your weekly financial summary is ready to be downloaded.', + timestamp: `5 ${t('common:notifications_hoursAgo')}`, + isRead: true, + }, + ]); + + const unreadCount = notifications.filter((n) => !n.isRead).length; + + const handleMarkAllAsRead = () => { + setNotifications((prev) => prev.map((n) => ({ ...n, isRead: true }))); + }; + + return ( + + + {/* Trigger wrapped around the Bell icon */} + setOpened((o) => !o)} style={{ cursor: 'pointer' }}> + + {children} + + + + + + + + {t('common:notifications_title')} + + {unreadCount > 0 && ( + + + {t('common:notifications_markAllAsRead')} + + + )} + + + + {notifications.length === 0 ? ( +
+ + + + {t('common:notifications_emptyState')} + + +
+ ) : ( + notifications.map((item) => ( + + + {item.title} + + + {item.description} + + + {item.timestamp} + + + } + leftSection={ + + + + + + } + active={!item.isRead} + variant="light" + style={{ + borderBottom: '1px solid var(--mantine-color-default-border)', + }} + /> + )) + )} +
+ + {notifications.length > 0 && ( + + + + )} +
+
+ ); +} diff --git a/packages/core-i18n/src/locales/en/common.json b/packages/core-i18n/src/locales/en/common.json index 498ac39..2646c98 100644 --- a/packages/core-i18n/src/locales/en/common.json +++ b/packages/core-i18n/src/locales/en/common.json @@ -19,6 +19,14 @@ "switchWorkspace": "Switch Workspace", "signOut": "Sign Out", "english": "English", - "indonesian": "Indonesia" + "indonesian": "Indonesia", + "notifications_title": "Notifications", + "notifications_unread": "Unread", + "notifications_all": "All", + "notifications_markAllAsRead": "Mark all as read", + "notifications_viewAll": "View all notifications", + "notifications_emptyState": "No new notifications", + "notifications_justNow": "Just now", + "notifications_hoursAgo": "hours ago" } } \ No newline at end of file diff --git a/packages/core-i18n/src/locales/id/common.json b/packages/core-i18n/src/locales/id/common.json index 0fe528e..0f14717 100644 --- a/packages/core-i18n/src/locales/id/common.json +++ b/packages/core-i18n/src/locales/id/common.json @@ -19,6 +19,14 @@ "switchWorkspace": "Ganti Ruang Kerja", "signOut": "Keluar", "english": "Inggris", - "indonesian": "Indonesia" + "indonesian": "Bahasa Indonesia", + "notifications_title": "Notifikasi", + "notifications_unread": "Belum dibaca", + "notifications_all": "Semua", + "notifications_markAllAsRead": "Tandai semua dibaca", + "notifications_viewAll": "Lihat semua notifikasi", + "notifications_emptyState": "Tidak ada notifikasi baru", + "notifications_justNow": "Baru saja", + "notifications_hoursAgo": "jam lalu" } } \ No newline at end of file