B2B-88: add starter kit structure and elements

This commit is contained in:
devmc-ee
2025-06-08 16:18:30 +03:00
parent 657a36a298
commit e7b25600cb
1280 changed files with 77893 additions and 5688 deletions

View File

@@ -0,0 +1,139 @@
'use client';
import { useCallback } from 'react';
import type {
Provider,
SignInWithOAuthCredentials,
} from '@supabase/supabase-js';
import { useSignInWithProvider } from '@kit/supabase/hooks/use-sign-in-with-provider';
import { If } from '@kit/ui/if';
import { LoadingOverlay } from '@kit/ui/loading-overlay';
import { Trans } from '@kit/ui/trans';
import { AuthErrorAlert } from './auth-error-alert';
import { AuthProviderButton } from './auth-provider-button';
/**
* @name OAUTH_SCOPES
* @description
* The OAuth scopes are used to specify the permissions that the application is requesting from the user.
*
* Please add your OAuth providers here and the scopes you want to use.
*
* @see https://supabase.com/docs/guides/auth/social-login
*/
const OAUTH_SCOPES: Partial<Record<Provider, string>> = {
azure: 'email',
keycloak: 'openid',
// add your OAuth providers here
};
export const OauthProviders: React.FC<{
inviteToken?: string;
shouldCreateUser: boolean;
enabledProviders: Provider[];
queryParams?: Record<string, string>;
paths: {
callback: string;
returnPath: string;
};
}> = (props) => {
const signInWithProviderMutation = useSignInWithProvider();
// we make the UI "busy" until the next page is fully loaded
const loading = signInWithProviderMutation.isPending;
const onSignInWithProvider = useCallback(
async (signInRequest: () => Promise<unknown>) => {
const credential = await signInRequest();
if (!credential) {
return Promise.reject(new Error(`No credential returned`));
}
},
[],
);
const enabledProviders = props.enabledProviders;
if (!enabledProviders?.length) {
return null;
}
return (
<>
<If condition={loading}>
<LoadingOverlay />
</If>
<div className={'flex w-full flex-1 flex-col space-y-3'}>
<div className={'flex-col space-y-2'}>
{enabledProviders.map((provider) => {
return (
<AuthProviderButton
key={provider}
providerId={provider}
onClick={() => {
const origin = window.location.origin;
const queryParams = new URLSearchParams();
if (props.paths.returnPath) {
queryParams.set('next', props.paths.returnPath);
}
if (props.inviteToken) {
queryParams.set('invite_token', props.inviteToken);
}
const redirectPath = [
props.paths.callback,
queryParams.toString(),
].join('?');
const redirectTo = [origin, redirectPath].join('');
const scopes = OAUTH_SCOPES[provider] ?? undefined;
const credentials = {
provider,
options: {
redirectTo,
queryParams: props.queryParams,
scopes,
},
} satisfies SignInWithOAuthCredentials;
return onSignInWithProvider(() =>
signInWithProviderMutation.mutateAsync(credentials),
);
}}
>
<Trans
i18nKey={'auth:signInWithProvider'}
values={{
provider: getProviderName(provider),
}}
/>
</AuthProviderButton>
);
})}
</div>
<AuthErrorAlert error={signInWithProviderMutation.error} />
</div>
</>
);
};
function getProviderName(providerId: string) {
const capitalize = (value: string) =>
value.slice(0, 1).toUpperCase() + value.slice(1);
if (providerId.endsWith('.com')) {
return capitalize(providerId.split('.com')[0]!);
}
return capitalize(providerId);
}