2.18.0: New Invitation flow, refactored Database Webhooks, new ShadCN UI Components (#384)

* Streamlined invitations flow
* Removed web hooks in favor of handling logic directly in server actions
* Added new Shadcn UI Components
This commit is contained in:
Giancarlo Buomprisco
2025-10-05 17:54:16 +08:00
committed by GitHub
parent 195cf41680
commit 2e20d3e76f
60 changed files with 3760 additions and 1009 deletions

View File

@@ -28,13 +28,11 @@ import { useLastAuthMethod } from '../hooks/use-last-auth-method';
import { TermsAndConditionsFormField } from './terms-and-conditions-form-field';
export function MagicLinkAuthContainer({
inviteToken,
redirectUrl,
shouldCreateUser,
defaultValues,
displayTermsCheckbox,
}: {
inviteToken?: string;
redirectUrl: string;
shouldCreateUser: boolean;
displayTermsCheckbox?: boolean;
@@ -63,10 +61,6 @@ export function MagicLinkAuthContainer({
const onSubmit = ({ email }: { email: string }) => {
const url = new URL(redirectUrl);
if (inviteToken) {
url.searchParams.set('invite_token', inviteToken);
}
const emailRedirectTo = url.href;
const promise = async () => {

View File

@@ -32,7 +32,6 @@ const OAUTH_SCOPES: Partial<Record<Provider, string>> = {
};
export const OauthProviders: React.FC<{
inviteToken?: string;
shouldCreateUser: boolean;
enabledProviders: Provider[];
queryParams?: Record<string, string>;
@@ -86,10 +85,6 @@ export const OauthProviders: React.FC<{
queryParams.set('next', props.paths.returnPath);
}
if (props.inviteToken) {
queryParams.set('invite_token', props.inviteToken);
}
const redirectPath = [
props.paths.callback,
queryParams.toString(),

View File

@@ -36,7 +36,6 @@ const OtpSchema = z.object({ token: z.string().min(6).max(6) });
type OtpSignInContainerProps = {
shouldCreateUser: boolean;
inviteToken?: string;
};
export function OtpSignInContainer(props: OtpSignInContainerProps) {
@@ -80,19 +79,9 @@ export function OtpSignInContainer(props: OtpSignInContainerProps) {
recordAuthMethod('otp', { email });
// on sign ups we redirect to the app home
const inviteToken = props.inviteToken;
const next = params.get('next') ?? '/home';
if (inviteToken) {
const params = new URLSearchParams({
invite_token: inviteToken,
next,
});
router.replace(`/join?${params.toString()}`);
} else {
router.replace(next);
}
router.replace(next);
};
if (isEmailStep) {

View File

@@ -18,8 +18,6 @@ import { OtpSignInContainer } from './otp-sign-in-container';
import { PasswordSignInContainer } from './password-sign-in-container';
export function SignInMethodsContainer(props: {
inviteToken?: string;
paths: {
callback: string;
joinTeam: string;
@@ -40,22 +38,10 @@ export function SignInMethodsContainer(props: {
: '';
const onSignIn = useCallback(() => {
// if the user has an invite token, we should join the team
if (props.inviteToken) {
const searchParams = new URLSearchParams({
invite_token: props.inviteToken,
});
const returnPath = props.paths.returnPath || '/home';
const joinTeamPath = props.paths.joinTeam + '?' + searchParams.toString();
router.replace(joinTeamPath);
} else {
const returnPath = props.paths.returnPath || '/home';
// otherwise, we should redirect to the return path
router.replace(returnPath);
}
}, [props.inviteToken, props.paths.joinTeam, props.paths.returnPath, router]);
router.replace(returnPath);
}, [props.paths.returnPath, router]);
return (
<>
@@ -67,17 +53,13 @@ export function SignInMethodsContainer(props: {
<If condition={props.providers.magicLink}>
<MagicLinkAuthContainer
inviteToken={props.inviteToken}
redirectUrl={redirectUrl}
shouldCreateUser={false}
/>
</If>
<If condition={props.providers.otp}>
<OtpSignInContainer
inviteToken={props.inviteToken}
shouldCreateUser={false}
/>
<OtpSignInContainer shouldCreateUser={false} />
</If>
<If condition={props.providers.oAuth.length}>
@@ -95,7 +77,6 @@ export function SignInMethodsContainer(props: {
<OauthProviders
enabledProviders={props.providers.oAuth}
inviteToken={props.inviteToken}
shouldCreateUser={false}
paths={{
callback: props.paths.callback,

View File

@@ -3,7 +3,6 @@
import type { Provider } from '@supabase/supabase-js';
import { isBrowser } from '@kit/shared/utils';
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
import { If } from '@kit/ui/if';
import { Separator } from '@kit/ui/separator';
import { Trans } from '@kit/ui/trans';
@@ -28,7 +27,6 @@ export function SignUpMethodsContainer(props: {
};
displayTermsCheckbox?: boolean;
inviteToken?: string;
}) {
const redirectUrl = getCallbackUrl(props);
const defaultValues = getDefaultValues();
@@ -38,10 +36,6 @@ export function SignUpMethodsContainer(props: {
{/* Show hint if user might already have an account */}
<ExistingAccountHint />
<If condition={props.inviteToken}>
<InviteAlert />
</If>
<If condition={props.providers.password}>
<EmailPasswordSignUpContainer
emailRedirectTo={redirectUrl}
@@ -51,15 +45,11 @@ export function SignUpMethodsContainer(props: {
</If>
<If condition={props.providers.otp}>
<OtpSignInContainer
inviteToken={props.inviteToken}
shouldCreateUser={true}
/>
<OtpSignInContainer shouldCreateUser={true} />
</If>
<If condition={props.providers.magicLink}>
<MagicLinkAuthContainer
inviteToken={props.inviteToken}
redirectUrl={redirectUrl}
shouldCreateUser={true}
defaultValues={defaultValues}
@@ -82,7 +72,6 @@ export function SignUpMethodsContainer(props: {
<OauthProviders
enabledProviders={props.providers.oAuth}
inviteToken={props.inviteToken}
shouldCreateUser={true}
paths={{
callback: props.paths.callback,
@@ -99,8 +88,6 @@ function getCallbackUrl(props: {
callback: string;
appHome: string;
};
inviteToken?: string;
}) {
if (!isBrowser()) {
return '';
@@ -110,10 +97,6 @@ function getCallbackUrl(props: {
const origin = window.location.origin;
const url = new URL(redirectPath, origin);
if (props.inviteToken) {
url.searchParams.set('invite_token', props.inviteToken);
}
const searchParams = new URLSearchParams(window.location.search);
const next = searchParams.get('next');
@@ -130,27 +113,8 @@ function getDefaultValues() {
}
const searchParams = new URLSearchParams(window.location.search);
const inviteToken = searchParams.get('invite_token');
if (!inviteToken) {
return { email: '' };
}
return {
email: searchParams.get('email') ?? '',
};
}
function InviteAlert() {
return (
<Alert variant={'info'}>
<AlertTitle>
<Trans i18nKey={'auth:inviteAlertHeading'} />
</AlertTitle>
<AlertDescription>
<Trans i18nKey={'auth:inviteAlertBody'} />
</AlertDescription>
</Alert>
);
}