'use client' /** * This Source Code is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright (c) Infonomic Company Limited */ /** * Admin sign-in form. * * Client component — collects email + password, calls the `adminSignIn` * server fn, and on success navigates to a safe caller-supplied destination. * On failure renders a generic "Invalid * credentials" alert; the provider equalises timing between * unknown-email and wrong-password so the UI doesn't distinguish the two. * * Stable override handles: `.byline-sign-in-card`, `.byline-sign-in-alert`, * `.byline-sign-in-form`, `.byline-sign-in-fields`, * `.byline-sign-in-actions`, `.byline-sign-in-button`, * `.byline-sign-in-home-link`. */ import { type FormEvent, useState } from 'react' import { getAdminConfig } from '@byline/core' import { useTranslation } from '@byline/i18n/react' import { Alert, Button, Card, Input, InputPassword, LoaderEllipsis } from '@byline/ui/react' import cx from 'clsx' import { useBylineAdminServices } from '../../../services/admin-services-context.js' import { normalizeRootRelativeRedirect, resolveSignInFormRedirect } from '../safe-redirect.js' import styles from './sign-in-form.module.css' export interface SignInFormProps { /** Host-validated root-relative destination after successful sign-in. */ redirectTo?: string /** * Optional plain "Home" link rendered on the left of the action row. * Typically the host's client-safe canonical site URL so signed-out admins * can navigate back to the public site without typing the URL. */ homeUrl?: string } export function SignInForm({ redirectTo, homeUrl }: SignInFormProps) { const { adminSignIn } = useBylineAdminServices() const { t } = useTranslation('byline-admin') const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [pending, setPending] = useState(false) const [error, setError] = useState(null) async function handleSubmit(event: FormEvent) { event.preventDefault() if (pending) return if (email.trim().length === 0 || password.length === 0) { setError(t('auth.signIn.errors.empty')) return } const destination = resolveSignInFormRedirect(redirectTo, getAdminConfig().routes.admin) setPending(true) setError(null) try { await adminSignIn({ data: { email: email.trim(), password } }) } catch (err) { console.warn('sign-in failed', err) setError(t('auth.signIn.errors.invalidCredentials')) setPending(false) return } // Keep navigation outside the credential error boundary. A browser-level // navigation failure must not relabel a successful sign-in as bad credentials. window.location.assign(normalizeRootRelativeRedirect(destination) ?? '/') } return (

{t('auth.signIn.title')}

{t('auth.signIn.description')} {error && ( {error} )}
setEmail(event.currentTarget.value)} disabled={pending} /> setPassword(event.currentTarget.value)} disabled={pending} />
{homeUrl && ( {t('common.actions.home')} )}
) }