feat: add Toaster, notification bell, search, unread badges, last login, recent rooms, hotel alerts, quick room entry, friend requests in top bar
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 1m53s

This commit is contained in:
openhands committed 2026-07-26 13:17:51 +02:00
1 parent a906d671fb
commit 3284e5a915
5 files changed
+484 -19

No files matched your search

+68 -16
View File
@@ -9,6 +9,7 @@ import { NavbarColorPicker } from "@/components/navbar-color-picker";
import { ThemeSwitcher } from "@/components/theme-switcher";
import { resolveHotelName } from "@/lib/hotel-name";
import { canAccess, getApiAdminContext, PERMS } from "@/lib/permissions";
import { prisma } from "@/lib/prisma";
export async function Navigation({ session }: { session: Session | null }) {
const t = await getTranslations("nav");
@@ -24,11 +25,7 @@ export async function Navigation({ session }: { session: Session | null }) {
ctx.session.user.rank,
);
showMod =
canAccess(
ctx.permissions,
PERMS.MOD_DASHBOARD,
ctx.session.user.rank,
) ||
canAccess(ctx.permissions, PERMS.MOD_DASHBOARD, ctx.session.user.rank) ||
canAccess(
ctx.permissions,
PERMS.MODERATION_VIEW,
@@ -39,6 +36,19 @@ export async function Navigation({ session }: { session: Session | null }) {
}
}
let unreadMessages = 0;
let pendingFriendRequests = 0;
if (session?.user?.id) {
const id = Number(session.user.id);
try {
[unreadMessages, pendingFriendRequests] = await Promise.all([
prisma.messengerOffline.count({ where: { userId: id } }),
prisma.messengerFriendrequests.count({ where: { userToId: id } }),
]);
} catch {
}
}
return (
<nav
className="sticky top-0 z-40"
@@ -156,7 +166,7 @@ export async function Navigation({ session }: { session: Session | null }) {
{session?.user ? (
<>
<Link href="/friends" className="nav-item">
<Link href="/friends" className="nav-item relative">
<Image
src="/assets/images/icons/navigation/goody.png"
alt=""
@@ -164,9 +174,9 @@ export async function Navigation({ session }: { session: Session | null }) {
height={20}
className="mr-1.5"
/>
{t("friends")}
<span>{t("friends")}</span>
</Link>
<Link href="/messages" className="nav-item">
<Link href="/messages" className="nav-item relative">
<Image
src="/assets/images/icons/navigation/bb.png"
alt=""
@@ -174,7 +184,20 @@ export async function Navigation({ session }: { session: Session | null }) {
height={20}
className="mr-1.5"
/>
{t("messages")}
<span>{t("messages")}</span>
{(unreadMessages + pendingFriendRequests) > 0 && (
<span
className="absolute -top-1.5 -right-1.5 flex min-w-[18px] h-[18px] items-center justify-center rounded-full px-1 text-[10px] font-bold leading-none"
style={{
background: "var(--color-danger)",
color: "white",
}}
>
{unreadMessages + pendingFriendRequests > 9
? "9+"
: unreadMessages + pendingFriendRequests}
</span>
)}
</Link>
</>
) : null}
@@ -206,6 +229,42 @@ export async function Navigation({ session }: { session: Session | null }) {
) : null}
</div>
{/* Desktop: search + controls */}
<div className="ml-auto hidden md:flex items-center gap-x-2">
{session?.user ? (
<form
action="/search"
method="GET"
className="flex items-center gap-1"
>
<input
type="text"
name="q"
placeholder="Search users..."
className="w-32 rounded-lg border px-2.5 py-1.5 text-xs font-medium transition-all focus:w-40"
style={{
background: "var(--color-background)",
color: "var(--color-text-readable)",
borderColor: "color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
}}
/>
<button
type="submit"
className="rounded-lg px-2 py-1.5 text-xs font-bold transition-colors"
style={{
background: "color-mix(in srgb, var(--color-primary) 20%, transparent)",
color: "var(--color-primary-readable, var(--color-primary))",
}}
>
Search
</button>
</form>
) : null}
<LanguageSwitcher />
<NavbarColorPicker />
<ThemeSwitcher />
</div>
{/* Mobile: hamburger menu (details/summary) */}
<MobileNav
menuLabel={t("openMenu") || "Open menu"}
@@ -397,13 +456,6 @@ export async function Navigation({ session }: { session: Session | null }) {
Atom
</span>
</Link>
{/* Desktop: controls */}
<div className="ml-auto hidden md:flex items-center gap-x-1">
<LanguageSwitcher />
<NavbarColorPicker />
<ThemeSwitcher />
</div>
</div>
</nav>
);