feat: introduce AuthConnectButton component for streamlined authentication actions
Some checks are pending
E2E / Playwright e2e (push) Waiting to run

- Added AuthConnectButton component to centralize and simplify authentication button rendering across various forms.
- Updated existing authentication components to utilize AuthConnectButton, enhancing code reusability and maintainability.
- Refactored CSS styles for consistent button appearance and layout in authentication flows.
This commit is contained in:
R3D347HR4Y 2026-06-19 22:54:04 +02:00
parent 359931c2f3
commit be9133e220
5 changed files with 112 additions and 42 deletions

View File

@ -936,9 +936,15 @@ html[data-route-scope='drive'] body {
}
}
.ultimail-login-connect-action {
display: flex;
width: 100%;
justify-content: center;
}
.ultimail-login .ultimail-login-connect-border {
position: relative;
display: inline-flex;
display: flex;
width: 100%;
padding: 2px;
border-radius: 9999px;
@ -1163,6 +1169,11 @@ html[data-route-scope='drive'] body {
.ultimail-login .ultimail-login-connect-border {
width: auto;
}
.ultimail-login-connect-action .ultimail-login-connect-border,
.ultimail-login-card-frame .ultimail-login-connect-border {
width: 100%;
}
}
@media (prefers-reduced-motion: reduce) {

View File

@ -0,0 +1,66 @@
"use client"
import Link from "next/link"
import { cn } from "@/lib/utils"
type AuthConnectButtonCommon = {
children: React.ReactNode
className?: string
}
type AuthConnectButtonAsButton = AuthConnectButtonCommon &
React.ButtonHTMLAttributes<HTMLButtonElement> & {
href?: undefined
}
type AuthConnectButtonAsAnchor = AuthConnectButtonCommon &
React.AnchorHTMLAttributes<HTMLAnchorElement> & {
href: string
}
type AuthConnectButtonAsLink = AuthConnectButtonCommon &
Omit<React.ComponentProps<typeof Link>, "href"> & {
href: string
as: "link"
}
export type AuthConnectButtonProps =
| AuthConnectButtonAsButton
| AuthConnectButtonAsAnchor
| AuthConnectButtonAsLink
/** UltiSpace gradient CTA — full width, centered in auth flows. */
export function AuthConnectButton(props: AuthConnectButtonProps) {
const { children, className } = props
const btnClass = cn("ultimail-login-connect-btn w-full", className)
let control: React.ReactNode
if ("as" in props && props.as === "link") {
const { as: _as, href, className: _c, children: _ch, ...linkProps } = props
control = (
<Link href={href} className={btnClass} {...linkProps}>
{children}
</Link>
)
} else if ("href" in props && props.href) {
const { href, className: _c, children: _ch, ...anchorProps } = props
control = (
<a href={href} className={btnClass} {...anchorProps}>
{children}
</a>
)
} else {
const { className: _c, children: _ch, ...buttonProps } = props
control = (
<button className={btnClass} {...buttonProps}>
{children}
</button>
)
}
return (
<div className="ultimail-login-connect-action">
<div className="ultimail-login-connect-border">{control}</div>
</div>
)
}

View File

@ -1,9 +1,9 @@
"use client"
import { useCallback, useEffect, useMemo, useState } from "react"
import Link from "next/link"
import { Loader2 } from "lucide-react"
import { AuthCard } from "@/components/auth/auth-card"
import { AuthConnectButton } from "@/components/auth/auth-connect-button"
import { FlowChallengeForm } from "@/components/auth/flow-challenge-form"
import {
AUTH_FLOW_SLUGS,
@ -131,18 +131,16 @@ export function AuthFlowPage({
Redirection en cours
</p>
) : null}
<div className="ultimail-login-connect-border w-full">
{successExternal ? (
<a href={successHref} className="ultimail-login-connect-btn">
<AuthConnectButton href={successHref}>
{successActionLabel}
</a>
</AuthConnectButton>
) : (
<Link href={successHref} className="ultimail-login-connect-btn">
<AuthConnectButton as="link" href={successHref}>
{successActionLabel}
</Link>
</AuthConnectButton>
)}
</div>
</div>
) : (
<FlowChallengeForm
challenge={challenge}

View File

@ -2,6 +2,7 @@
import { useEffect, useMemo, useRef, useState } from "react"
import { Loader2 } from "lucide-react"
import { AuthConnectButton } from "@/components/auth/auth-connect-button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import type { FlowChallenge } from "@/lib/auth/flow-api"
@ -151,11 +152,10 @@ export function FlowChallengeForm({
) : null}
{isKnownComponent(component) ? (
<div className="ultimail-login-connect-border pt-1">
<button
<AuthConnectButton
type="submit"
disabled={submitting}
className="ultimail-login-connect-btn w-full disabled:opacity-60"
className="disabled:opacity-60"
>
{submitting ? (
<>
@ -165,8 +165,7 @@ export function FlowChallengeForm({
) : (
primaryAction
)}
</button>
</div>
</AuthConnectButton>
) : null}
</form>
)

View File

@ -1,8 +1,8 @@
"use client"
import Link from "next/link"
import { Sparkles } from "lucide-react"
import { AuthCard } from "@/components/auth/auth-card"
import { AuthConnectButton } from "@/components/auth/auth-connect-button"
type LoginFormProps = {
loginHref: string
@ -25,26 +25,22 @@ export function LoginForm({
<div className="flex w-full flex-col gap-3 text-center text-sm text-muted-foreground">
<p>
Pas encore de compte ?{" "}
<Link className="font-medium text-primary underline" href={signupHref}>
<a className="font-medium text-primary underline" href={signupHref}>
Créer un compte
</Link>
</a>
</p>
<p>
<Link className="font-medium text-primary underline" href={forgotPasswordHref}>
<a className="font-medium text-primary underline" href={forgotPasswordHref}>
Mot de passe oublié ?
</Link>
</a>
</p>
</div>
}
>
<div className="flex justify-center">
<div className="ultimail-login-connect-border w-full">
<a href={loginHref} className="ultimail-login-connect-btn">
<AuthConnectButton href={loginHref}>
<Sparkles className="size-4 shrink-0" strokeWidth={2} aria-hidden />
Se connecter avec UltiSpace
</a>
</div>
</div>
</AuthConnectButton>
</AuthCard>
)
}