| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 |
- "use client";
- import React from "react";
- import { logout } from "@/lib/frontend/apiClient";
- import { buildLoginUrl, LOGIN_REASONS } from "@/lib/frontend/authRedirect";
- import { Button } from "@/components/ui/button";
- /**
- * LogoutButton (RHL-020)
- *
- * Responsibilities:
- * - Call apiClient.logout() to clear the HTTP-only session cookie.
- * - Then redirect to /login?reason=logged-out.
- *
- * Important test/runtime note:
- * - We intentionally avoid next/navigation hooks here.
- * - Some unit tests render AppShell via react-dom/server without Next.js runtime.
- * - Using window.location inside the click handler avoids needing router context
- * during server rendering (handler is not invoked in SSR tests).
- *
- * UX rule:
- * - All user-facing text must be German.
- */
- export default function LogoutButton() {
- const [isLoggingOut, setIsLoggingOut] = React.useState(false);
- async function handleLogout() {
- if (isLoggingOut) return;
- setIsLoggingOut(true);
- try {
- // Backend endpoint is idempotent; even if no cookie exists it returns ok.
- await logout();
- } catch (err) {
- // If logout fails due to network issues, we still redirect to login.
- // This keeps UX predictable; user can log in again if needed.
- console.error("[LogoutButton] logout failed:", err);
- }
- const loginUrl = buildLoginUrl({ reason: LOGIN_REASONS.LOGGED_OUT });
- // Replace so "Back" won't bring the user into a protected page.
- window.location.replace(loginUrl);
- }
- return (
- <Button
- variant="outline"
- size="sm"
- type="button"
- disabled={isLoggingOut}
- aria-disabled={isLoggingOut ? "true" : "false"}
- onClick={handleLogout}
- title={isLoggingOut ? "Abmeldung läuft…" : "Abmelden"}
- >
- {isLoggingOut ? "Abmeldung…" : "Abmelden"}
- </Button>
- );
- }
|