Files
core/src/features/auth/AuthPage.client.js
T
hykocx abd9d651dc feat(auth): add user invitation flow with account setup
- add `createAccountSetup`, `verifyAccountSetupToken`, `deleteAccountSetupToken` to verifications core
- add `completeAccountSetup` function to auth core for password creation on invite
- add `InvitationEmail` template for sending invite links
- add `SetupAccountPage` client page for invited users to set their password
- add `UserCreateModal` admin component to invite new users
- wire invitation action and API endpoint in auth feature
- update admin `UsersPage` to include user creation modal
- update auth and admin README docs
2026-04-25 09:03:15 -04:00

95 lines
3.1 KiB
JavaScript

'use client';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import LoginPage from './pages/LoginPage.client.js';
import RegisterPage from './pages/RegisterPage.client.js';
import ForgotPasswordPage from './pages/ForgotPasswordPage.client.js';
import ResetPasswordPage from './pages/ResetPasswordPage.client.js';
import ConfirmEmailPage from './pages/ConfirmEmailPage.client.js';
import LogoutPage from './pages/LogoutPage.client.js';
import SetupAccountPage from './pages/SetupAccountPage.client.js';
const PAGE_COMPONENTS = {
login: LoginPage,
register: RegisterPage,
forgot: ForgotPasswordPage,
reset: ResetPasswordPage,
confirm: ConfirmEmailPage,
logout: LogoutPage,
setup: SetupAccountPage,
};
export default function AuthPage({
params,
searchParams,
registerAction,
loginAction,
forgotPasswordAction,
resetPasswordAction,
verifyEmailAction,
setupAccountAction,
logoutAction,
setSessionCookieAction,
redirectAfterLogin = '/',
currentUser = null,
}) {
const router = useRouter();
const [currentPage, setCurrentPage] = useState(null);
const [email, setEmail] = useState('');
const [token, setToken] = useState('');
useEffect(() => {
const fromParams = params?.auth?.[0];
if (fromParams) { setCurrentPage(fromParams); return; }
if (typeof window !== 'undefined') {
const match = window.location.pathname.match(/\/auth\/([^\/?]+)/);
setCurrentPage(match ? match[1] : 'login');
} else {
setCurrentPage('login');
}
}, [params]);
useEffect(() => {
const run = async () => {
// searchParams became a Promise in Next.js 15 — resolve both forms.
const resolved = searchParams && typeof searchParams.then === 'function'
? await searchParams
: searchParams;
const urlParams = typeof window !== 'undefined'
? new URLSearchParams(window.location.search)
: null;
setEmail(resolved?.email || urlParams?.get('email') || '');
setToken(resolved?.token || urlParams?.get('token') || '');
};
run();
}, [searchParams]);
const navigate = (page) => router.push(`/auth/${page}`);
if (!currentPage) return null;
const Page = PAGE_COMPONENTS[currentPage] || LoginPage;
const common = { onNavigate: navigate, currentUser };
switch (Page) {
case LoginPage:
return <Page {...common} onSubmit={loginAction} onSetSessionCookie={setSessionCookieAction} redirectAfterLogin={redirectAfterLogin} />;
case RegisterPage:
return <Page {...common} onSubmit={registerAction} />;
case ForgotPasswordPage:
return <Page {...common} onSubmit={forgotPasswordAction} />;
case ResetPasswordPage:
return <Page {...common} onSubmit={resetPasswordAction} email={email} token={token} />;
case ConfirmEmailPage:
return <Page {...common} onSubmit={verifyEmailAction} email={email} token={token} />;
case SetupAccountPage:
return <Page {...common} onSubmit={setupAccountAction} email={email} token={token} />;
case LogoutPage:
return <Page onLogout={logoutAction} onSetSessionCookie={setSessionCookieAction} />;
default:
return null;
}
}