feat(notification): 實作通知輪詢與優化顯示名稱

- 新增通知輪詢 API 與前端自動更新機制
- 修正生產工單單號格式為 PRO-YYYYMMDD-XX
- 確保通知顯示實際建立者名稱而非系統
This commit is contained in:
2026-02-12 17:13:09 +08:00
parent 299602d3b1
commit 882091ce5f
14 changed files with 528 additions and 47 deletions

View File

@@ -44,6 +44,7 @@ import { usePermission } from "@/hooks/usePermission";
import ApplicationLogo from "@/Components/ApplicationLogo";
import { generateLightestColor, generateLightColor, generateDarkColor, generateActiveColor } from "@/utils/colorUtils";
import { PageProps } from "@/types/global";
import NotificationDropdown from "@/Components/Header/NotificationDropdown";
interface MenuItem {
id: string;
@@ -491,47 +492,51 @@ export default function AuthenticatedLayout({
</div>
{/* User Menu */}
<DropdownMenu modal={false}>
<DropdownMenuTrigger className="flex items-center gap-2 outline-none group">
<div className="hidden md:flex flex-col items-end mr-1">
<span className="text-sm font-medium text-slate-700 group-hover:text-slate-900 transition-colors">
{user.name} ({user.username})
</span>
<span className="text-xs text-slate-500">
{user.role_labels?.[0] || user.roles?.[0] || '一般用戶'}
</span>
</div>
<div className="h-9 w-9 bg-slate-100 rounded-full flex items-center justify-center text-slate-600 group-hover:bg-primary-lightest group-hover:text-primary-main transition-all">
<User className="h-5 w-5" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56 z-[100]" sideOffset={8}>
<DropdownMenuLabel>{user.name} ({user.username})</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link
href={route('profile.edit')}
preserveScroll={true}
className="w-full flex items-center cursor-pointer text-slate-600 focus:bg-slate-100 focus:text-slate-900 group"
>
<Settings className="mr-2 h-4 w-4 text-slate-500 group-focus:text-slate-900" />
<span>使</span>
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link
href={route('logout')}
method="post"
as="button"
className="w-full flex items-center cursor-pointer text-red-600 focus:text-red-600 focus:bg-red-50"
>
<LogOut className="mr-2 h-4 w-4" />
<span></span>
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<div className="flex items-center gap-2">
<NotificationDropdown />
<DropdownMenu modal={false}>
<DropdownMenuTrigger className="flex items-center gap-2 outline-none group">
<div className="hidden md:flex flex-col items-end mr-1">
<span className="text-sm font-medium text-slate-700 group-hover:text-slate-900 transition-colors">
{user.name} ({user.username})
</span>
<span className="text-xs text-slate-500">
{user.role_labels?.[0] || user.roles?.[0] || '一般用戶'}
</span>
</div>
<div className="h-9 w-9 bg-slate-100 rounded-full flex items-center justify-center text-slate-600 group-hover:bg-primary-lightest group-hover:text-primary-main transition-all">
<User className="h-5 w-5" />
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56 z-[100]" sideOffset={8}>
<DropdownMenuLabel>{user.name} ({user.username})</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link
href={route('profile.edit')}
preserveScroll={true}
className="w-full flex items-center cursor-pointer text-slate-600 focus:bg-slate-100 focus:text-slate-900 group"
>
<Settings className="mr-2 h-4 w-4 text-slate-500 group-focus:text-slate-900" />
<span>使</span>
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link
href={route('logout')}
method="post"
as="button"
className="w-full flex items-center cursor-pointer text-red-600 focus:text-red-600 focus:bg-red-50"
>
<LogOut className="mr-2 h-4 w-4" />
<span></span>
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>
{/* Sidebar Desktop */}