ultisuite-client/components/mobile/native-login.tsx
R3D347HR4Y d6d18f911b
Some checks failed
E2E / Playwright e2e (push) Has been cancelled
Lots of stuff and mobile app
2026-06-17 00:13:28 +02:00

111 lines
3.5 KiB
TypeScript

"use client"
import { useState } from "react"
import { useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
} from "@/components/ui/card"
import { ServerPicker } from "@/components/mobile/server-picker"
import { nativeStartLogin } from "@/lib/auth/native-auth"
import { getRuntimeConfig } from "@/lib/runtime-config"
import { getAuthentikEnrollmentUrl } from "@/lib/auth/oidc-config"
import { cn } from "@/lib/utils"
const CARD_CLASS = cn(
"w-full gap-4 border-0 bg-transparent px-4 py-6 shadow-none",
"sm:gap-5 sm:bg-card sm:px-8 sm:py-8"
)
/** Native login experience: server picker, then OIDC PKCE via the system browser. */
export function NativeLogin({ returnTo }: { returnTo: string }) {
const router = useRouter()
const [hasConfig, setHasConfig] = useState(() => Boolean(getRuntimeConfig()))
const [busy, setBusy] = useState(false)
const [error, setError] = useState<string | null>(null)
if (!hasConfig) {
return <ServerPicker onSelected={() => setHasConfig(true)} />
}
const cfg = getRuntimeConfig()
async function login() {
setBusy(true)
setError(null)
try {
await nativeStartLogin({ returnTo })
router.replace(returnTo.startsWith("/") ? returnTo : "/mail/inbox")
} catch (err) {
setError(err instanceof Error ? err.message : "Connexion échouée.")
} finally {
setBusy(false)
}
}
return (
<div className="flex flex-1 flex-col items-center justify-center px-4">
<div className="mx-auto w-full max-w-sm">
<Card className={CARD_CLASS}>
<CardHeader className="gap-3 px-0 text-center">
<div className="flex flex-col items-center gap-3 py-2">
<img
src="/ultisuite-mark.svg"
alt=""
width={64}
height={64}
className="h-14 w-14 select-none"
aria-hidden
/>
<span className="text-xl font-bold tracking-tight">
Ulti<span className="text-[#4285F4]">Suite</span>
</span>
</div>
<CardDescription>
{cfg ? `Connecté à ${cfg.label}` : "Connecte-toi à ton compte Ulti."}
</CardDescription>
{error ? (
<p className="text-sm text-destructive" role="alert">
{error}
</p>
) : null}
</CardHeader>
<CardContent className="flex flex-col items-center gap-3 px-0">
<Button size="lg" className="w-full" disabled={busy} onClick={() => void login()}>
{busy ? "Connexion…" : "Se connecter"}
</Button>
<Button
size="sm"
variant="ghost"
className="w-full"
disabled={busy}
onClick={() => setHasConfig(false)}
>
Changer de serveur
</Button>
</CardContent>
<CardFooter className="px-0">
<p className="w-full text-center text-sm text-muted-foreground">
Pas encore de compte ?{" "}
<a
className="font-medium text-primary underline"
href={getAuthentikEnrollmentUrl()}
target="_blank"
rel="noreferrer"
>
Créer un compte
</a>
</p>
</CardFooter>
</Card>
</div>
</div>
)
}