Refactor join page and update packages
The join page's redirect functionality has been replaced with permanentRedirect. Adjustments were also made for handling conditional authentication and user account verification. Package upgrades were performed and the i18n client now checks if an instance is already initialized. The process tends to deliver performance benefits and enhancing the code readability.
This commit is contained in:
@@ -48,8 +48,10 @@ export class AuthPageObject {
|
||||
await this.page.click('button[type="submit"]');
|
||||
}
|
||||
|
||||
async visitConfirmEmailLink(email: string, params?: {
|
||||
async visitConfirmEmailLink(email: string, params: {
|
||||
deleteAfter: boolean
|
||||
} = {
|
||||
deleteAfter: true
|
||||
}) {
|
||||
return expect(async() => {
|
||||
const res = await this.mailbox.visitMailbox(email, params);
|
||||
|
||||
@@ -36,9 +36,9 @@ test.describe('Invitations', () => {
|
||||
// sign out and sign in with the first email
|
||||
await invitations.auth.signOut();
|
||||
|
||||
await invitations.auth.visitConfirmEmailLink(invites[0]!.email, {
|
||||
deleteAfter: true
|
||||
});
|
||||
await invitations.auth.visitConfirmEmailLink(invites[0]!.email);
|
||||
|
||||
console.log(`Signing up with ${firstEmail}`);
|
||||
|
||||
await invitations.auth.signUp({
|
||||
email: firstEmail,
|
||||
@@ -48,6 +48,8 @@ test.describe('Invitations', () => {
|
||||
|
||||
await invitations.auth.visitConfirmEmailLink(firstEmail);
|
||||
|
||||
console.log(`Accepting invitation as ${firstEmail}`);
|
||||
|
||||
await invitations.acceptInvitation();
|
||||
|
||||
await invitations.teamAccounts.openAccountsSelector();
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { GlobalLoader } from '@kit/ui/global-loader';
|
||||
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
export default withI18n(GlobalLoader);
|
||||
export default GlobalLoader;
|
||||
|
||||
@@ -24,6 +24,11 @@ interface Props {
|
||||
};
|
||||
}
|
||||
|
||||
const paths = {
|
||||
callback: pathsConfig.auth.callback,
|
||||
appHome: pathsConfig.app.home,
|
||||
};
|
||||
|
||||
function SignUpPage({ searchParams }: Props) {
|
||||
const inviteToken = searchParams.invite_token;
|
||||
|
||||
@@ -36,10 +41,7 @@ function SignUpPage({ searchParams }: Props) {
|
||||
<SignUpMethodsContainer
|
||||
providers={authConfig.providers}
|
||||
inviteToken={inviteToken}
|
||||
paths={{
|
||||
callback: pathsConfig.auth.callback,
|
||||
appHome: pathsConfig.app.home,
|
||||
}}
|
||||
paths={paths}
|
||||
/>
|
||||
|
||||
<div className={'justify-centers flex'}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
import { notFound, redirect } from 'next/navigation';
|
||||
import { notFound, permanentRedirect } from 'next/navigation';
|
||||
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
|
||||
@@ -18,7 +18,7 @@ import { JoinTeamService } from './_lib/server/join-team.service';
|
||||
|
||||
interface Context {
|
||||
searchParams: {
|
||||
invite_token: string;
|
||||
invite_token?: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@ async function JoinTeamAccountPage({ searchParams }: Context) {
|
||||
// redirect to the sign up page with the invite token
|
||||
// so that they will get back to this page after signing up
|
||||
if (auth.error ?? !auth.data) {
|
||||
redirect(pathsConfig.auth.signUp + '?invite_token=' + token);
|
||||
const path = `${pathsConfig.auth.signUp}?invite_token=${token}`;
|
||||
|
||||
permanentRedirect(path);
|
||||
}
|
||||
|
||||
const service = new JoinTeamService();
|
||||
@@ -53,16 +55,16 @@ async function JoinTeamAccountPage({ searchParams }: Context) {
|
||||
// the user is logged in, we can now check if the token is valid
|
||||
const invitation = await service.getInviteDataFromInviteToken(token);
|
||||
|
||||
// the invitation is not found or expired
|
||||
if (!invitation) {
|
||||
return <InviteNotFoundOrExpired />;
|
||||
}
|
||||
|
||||
// we need to verify the user isn't already in the account
|
||||
const isInAccount = await service.isCurrentUserAlreadyInAccount(
|
||||
invitation.account.id,
|
||||
);
|
||||
const isSignedInUserPartOfAccount =
|
||||
await service.isCurrentUserAlreadyInAccount(invitation.account.id);
|
||||
|
||||
if (isInAccount) {
|
||||
if (isSignedInUserPartOfAccount) {
|
||||
const { getLogger } = await import('@kit/shared/logger');
|
||||
const logger = await getLogger();
|
||||
|
||||
@@ -76,12 +78,12 @@ async function JoinTeamAccountPage({ searchParams }: Context) {
|
||||
);
|
||||
|
||||
// if the user is already in the account redirect to the home page
|
||||
redirect(pathsConfig.app.home);
|
||||
permanentRedirect(pathsConfig.app.home);
|
||||
}
|
||||
|
||||
// if the user decides to sign in with a different account
|
||||
// we redirect them to the sign in page with the invite token
|
||||
const signOutNext = pathsConfig.auth.signIn + '?invite_token=' + token;
|
||||
const signOutNext = `${pathsConfig.auth.signIn}?invite_token=${token}`;
|
||||
|
||||
// once the user accepts the invitation, we redirect them to the account home page
|
||||
const accountHome = pathsConfig.app.accountHome.replace(
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { GlobalLoader } from '@kit/ui/global-loader';
|
||||
|
||||
export default GlobalLoader;
|
||||
@@ -39,7 +39,7 @@
|
||||
"@manypkg/cli": "^0.21.3",
|
||||
"@turbo/gen": "^1.13.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"pnpm": "^8.15.6",
|
||||
"pnpm": "^8.15.7",
|
||||
"prettier": "^3.2.5",
|
||||
"turbo": "^1.13.2",
|
||||
"typescript": "^5.4.5",
|
||||
|
||||
@@ -3,6 +3,8 @@ import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import resourcesToBackend from 'i18next-resources-to-backend';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
let clientInstance: i18n | null = null;
|
||||
|
||||
/**
|
||||
* Initialize the i18n instance on the client.
|
||||
* @param settings - the i18n settings
|
||||
@@ -12,6 +14,10 @@ export function initializeI18nClient(
|
||||
settings: InitOptions,
|
||||
resolver: (lang: string, namespace: string) => Promise<object>,
|
||||
): Promise<i18n> {
|
||||
if (clientInstance?.isInitialized) {
|
||||
return Promise.resolve(clientInstance);
|
||||
}
|
||||
|
||||
return new Promise<i18n>((resolve, reject) => {
|
||||
void i18next
|
||||
.use(initReactI18next)
|
||||
@@ -40,7 +46,9 @@ export function initializeI18nClient(
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(i18next);
|
||||
clientInstance = i18next;
|
||||
|
||||
resolve(clientInstance);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -14,6 +14,10 @@ export async function initializeServerI18n(
|
||||
) {
|
||||
const i18nInstance = createInstance();
|
||||
|
||||
if (i18nInstance.isInitialized) {
|
||||
return i18nInstance;
|
||||
}
|
||||
|
||||
await i18nInstance
|
||||
.use(initReactI18next)
|
||||
.use(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { If } from './if';
|
||||
import { LoadingOverlay } from './loading-overlay';
|
||||
import { TopLoadingBarIndicator } from './top-loading-bar-indicator';
|
||||
import { Trans } from './trans';
|
||||
@@ -8,21 +9,29 @@ export function GlobalLoader({
|
||||
children,
|
||||
displayLogo = false,
|
||||
fullPage = false,
|
||||
displaySpinner = true,
|
||||
displayTopLoadingBar = true,
|
||||
}: React.PropsWithChildren<{
|
||||
displayLogo?: boolean;
|
||||
fullPage?: boolean;
|
||||
displaySpinner?: boolean;
|
||||
displayTopLoadingBar?: boolean;
|
||||
}>) {
|
||||
const Text = children ?? <Trans i18nKey={'common:loading'} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopLoadingBarIndicator />
|
||||
<If condition={displayTopLoadingBar}>
|
||||
<TopLoadingBarIndicator />
|
||||
</If>
|
||||
|
||||
<div className={'flex flex-1 flex-col items-center justify-center py-48'}>
|
||||
<LoadingOverlay displayLogo={displayLogo} fullPage={fullPage}>
|
||||
{Text}
|
||||
</LoadingOverlay>
|
||||
</div>
|
||||
<If condition={displaySpinner}>
|
||||
<div className={'flex flex-1 flex-col items-center justify-center py-48'}>
|
||||
<LoadingOverlay displayLogo={displayLogo} fullPage={fullPage}>
|
||||
{Text}
|
||||
</LoadingOverlay>
|
||||
</div>
|
||||
</If>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
import { cn } from '../utils/cn';
|
||||
import { cn } from '../utils';
|
||||
import Spinner from './spinner';
|
||||
|
||||
export function LoadingOverlay({
|
||||
|
||||
780
pnpm-lock.yaml
generated
780
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user