AuthGate.jsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. "use client";
  2. import React from "react";
  3. import { Loader2, RefreshCw } from "lucide-react";
  4. import { useAuth } from "@/components/auth/authContext";
  5. import { Button } from "@/components/ui/button";
  6. import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
  7. import {
  8. Card,
  9. CardHeader,
  10. CardTitle,
  11. CardDescription,
  12. CardContent,
  13. CardFooter,
  14. } from "@/components/ui/card";
  15. /**
  16. * AuthGate
  17. *
  18. * Renders auth-related states *inside* the AppShell main area to avoid "blank spinner" screens.
  19. *
  20. * Why this exists:
  21. * - AuthProvider owns the session check and provides AuthContext.
  22. * - We want the AppShell frame (TopNav/sidebar) to remain stable while auth checks run.
  23. * - This component decides what the user sees inside the main content area.
  24. *
  25. * UX rule:
  26. * - All user-facing strings are German.
  27. *
  28. * @param {{ children: React.ReactNode }} props
  29. */
  30. export default function AuthGate({ children }) {
  31. const { status, error, retry } = useAuth();
  32. const canRetry = typeof retry === "function";
  33. if (status === "authenticated") {
  34. return children;
  35. }
  36. if (status === "error") {
  37. return (
  38. <Card>
  39. <CardHeader>
  40. <CardTitle>Sitzungsprüfung fehlgeschlagen</CardTitle>
  41. <CardDescription>
  42. Die Sitzung konnte nicht geprüft werden.
  43. </CardDescription>
  44. </CardHeader>
  45. <CardContent className="space-y-3">
  46. <Alert variant="destructive">
  47. <AlertTitle>Fehler</AlertTitle>
  48. <AlertDescription>
  49. {error ||
  50. "Bitte prüfen Sie Ihre Verbindung und versuchen Sie es erneut."}
  51. </AlertDescription>
  52. </Alert>
  53. </CardContent>
  54. <CardFooter className="flex flex-col gap-2 sm:flex-row sm:justify-end">
  55. <Button
  56. type="button"
  57. variant="outline"
  58. onClick={() => {
  59. if (canRetry) retry();
  60. else window.location.reload();
  61. }}
  62. >
  63. <RefreshCw className="h-4 w-4" />
  64. Erneut versuchen
  65. </Button>
  66. </CardFooter>
  67. </Card>
  68. );
  69. }
  70. // "unauthenticated" -> redirect happens in AuthProvider.
  71. if (status === "unauthenticated") {
  72. return (
  73. <Card>
  74. <CardHeader>
  75. <CardTitle>Weiterleitung</CardTitle>
  76. <CardDescription>
  77. Sie werden zum Login weitergeleitet.
  78. </CardDescription>
  79. </CardHeader>
  80. <CardContent>
  81. <div className="flex items-center gap-3 text-sm text-muted-foreground">
  82. <Loader2 className="h-4 w-4 animate-spin" />
  83. <span>Weiterleitung zum Login…</span>
  84. </div>
  85. </CardContent>
  86. </Card>
  87. );
  88. }
  89. // Default: loading (or unknown)
  90. return (
  91. <Card>
  92. <CardHeader>
  93. <CardTitle>Sitzung wird geprüft</CardTitle>
  94. <CardDescription>Bitte warten…</CardDescription>
  95. </CardHeader>
  96. <CardContent>
  97. <div className="flex items-center gap-3 text-sm text-muted-foreground">
  98. <Loader2 className="h-4 w-4 animate-spin" />
  99. <span>Sitzung wird geprüft…</span>
  100. </div>
  101. </CardContent>
  102. </Card>
  103. );
  104. }