Database: - notification_preferences table (user_id, tenant_id, reminder days, email/digest toggles) - notifications table (type, entity link, read/sent tracking, dedup index) Backend: - NotificationService with background goroutine checking reminders hourly - CheckDeadlineReminders: finds deadlines due in N days per user prefs, creates notifications - Overdue deadline detection and notification - Daily digest at 8am: compiles pending notifications into one email - SendEmail via `m mail send` CLI command - Deduplication: same notification type + entity + day = skip - API: GET/PATCH notifications, unread count, mark read/all-read - API: GET/PUT notification-preferences with upsert Frontend: - NotificationBell in header with unread count badge (polls every 30s) - Dropdown panel with notification list, type-colored dots, time-ago, entity links - Mark individual/all as read - NotificationSettings in Einstellungen page: reminder day toggles, email toggle, digest toggle
50 lines
1.5 KiB
TypeScript
50 lines
1.5 KiB
TypeScript
"use client";
|
|
|
|
import { createClient } from "@/lib/supabase/client";
|
|
import { TenantSwitcher } from "./TenantSwitcher";
|
|
import { NotificationBell } from "@/components/notifications/NotificationBell";
|
|
import { LogOut } from "lucide-react";
|
|
import { useRouter } from "next/navigation";
|
|
import { useEffect, useState } from "react";
|
|
|
|
export function Header() {
|
|
const [email, setEmail] = useState<string | null>(null);
|
|
const router = useRouter();
|
|
const supabase = createClient();
|
|
|
|
useEffect(() => {
|
|
supabase.auth.getUser().then(({ data: { user } }) => {
|
|
setEmail(user?.email ?? null);
|
|
});
|
|
}, [supabase.auth]);
|
|
|
|
async function handleLogout() {
|
|
await supabase.auth.signOut();
|
|
router.push("/login");
|
|
router.refresh();
|
|
}
|
|
|
|
return (
|
|
<header className="flex h-14 items-center justify-between border-b border-neutral-200 bg-white px-4">
|
|
{/* Spacer for mobile hamburger */}
|
|
<div className="w-8 lg:w-0" />
|
|
<div className="flex items-center gap-2 sm:gap-3">
|
|
<TenantSwitcher />
|
|
<NotificationBell />
|
|
{email && (
|
|
<span className="hidden text-sm text-neutral-500 sm:inline">
|
|
{email}
|
|
</span>
|
|
)}
|
|
<button
|
|
onClick={handleLogout}
|
|
title="Abmelden"
|
|
className="rounded-md p-1.5 text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-neutral-600"
|
|
>
|
|
<LogOut className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|