@stack-spot/auth-react 2.7.1 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/out/index.d.ts +26 -25
- package/out/index.js +164 -94
- package/out/index.js.map +1 -1
- package/out/index.mjs +165 -95
- package/out/index.mjs.map +1 -1
- package/package.json +1 -1
- package/rollup.config.mjs +1 -1
- package/src/Authenticated.tsx +2 -1
- package/src/IDPLogin.tsx +79 -0
- package/src/Login.tsx +48 -140
- package/src/SSOLogin.tsx +50 -0
- package/src/dictionary.ts +40 -0
- package/src/hooks.ts +8 -3
- package/src/index.ts +0 -1
- package/src/last-login-type.ts +20 -0
- package/src/types.ts +28 -0
package/src/SSOLogin.tsx
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Button, Flex, Input, Label, Text } from '@citric/core'
|
|
2
|
+
import { Github, Google } from '@citric/icons'
|
|
3
|
+
import { LoadingCircular } from '@citric/ui'
|
|
4
|
+
import { useTranslation } from './dictionary'
|
|
5
|
+
import { Provider } from './hooks'
|
|
6
|
+
import { Microsoft } from './provider-icons/Microsoft'
|
|
7
|
+
import { LoginType } from './types'
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
value: string,
|
|
11
|
+
onChange: (value: string) => void,
|
|
12
|
+
disabled: boolean,
|
|
13
|
+
loading: boolean,
|
|
14
|
+
hasProvider: boolean,
|
|
15
|
+
onChangeMode: (mode: LoginType) => void,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ButtonProviderProps {
|
|
19
|
+
provider: Provider,
|
|
20
|
+
loading: boolean,
|
|
21
|
+
login: (type: LoginType, provider?: Provider) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const providerIcons: Record<Provider, React.ReactElement> = {
|
|
25
|
+
github: <Github />,
|
|
26
|
+
google: <Google />,
|
|
27
|
+
microsoft: <Microsoft />,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const SSOLogin = ({ value, onChange, loading, disabled, hasProvider, onChangeMode }: Props) => {
|
|
31
|
+
const t = useTranslation()
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<Flex flexDirection='column' style={{ gap: '4px', marginTop: '4px' }}>
|
|
35
|
+
<Label htmlFor='email'>{t.label}</Label>
|
|
36
|
+
<Input id="email" type='email' name="email" value={value} onChange={e => onChange(e.target.value)} placeholder={t.placeholder} />
|
|
37
|
+
<Button colorScheme="primary" size='md' style={{ marginTop: '12px' }} disabled={disabled || loading}>
|
|
38
|
+
{loading && !hasProvider ? <LoadingCircular /> : <Text>{t.continue}</Text>}
|
|
39
|
+
</Button>
|
|
40
|
+
</Flex>
|
|
41
|
+
<p className="separator">
|
|
42
|
+
<Text appearance='microtext1' colorScheme='light.700'>{t.or}</Text>
|
|
43
|
+
</p>
|
|
44
|
+
<Text colorScheme="light.700" align="center">{t.socialLoginTitle}</Text>
|
|
45
|
+
<Button size='md' disabled={loading} colorScheme="light" onClick={() => onChangeMode('idp')}>
|
|
46
|
+
{t.socialLogin}
|
|
47
|
+
</Button>
|
|
48
|
+
</>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
2
|
+
|
|
3
|
+
const dictionary = {
|
|
4
|
+
en: {
|
|
5
|
+
welcome: 'Welcome to StackSpot AI',
|
|
6
|
+
loginWithEmail: 'Log in with your email.',
|
|
7
|
+
loginWithSocialAccount: 'Sign up or access your free trial with a social account',
|
|
8
|
+
label: 'Corporate email',
|
|
9
|
+
placeholder: 'email@company.com',
|
|
10
|
+
continue: 'Continue',
|
|
11
|
+
or: 'Or',
|
|
12
|
+
loginWith: 'Sign in with $0',
|
|
13
|
+
emailNotAllowedTitle: 'Your email is linked to an Enterprise account.',
|
|
14
|
+
emailNotAllowedSubtitle: "Please log in with your corporate email.",
|
|
15
|
+
socialLogin: 'Login or register with a social account',
|
|
16
|
+
corporateLoginTitle: 'Already have a StackSpot enterprise account?',
|
|
17
|
+
corporateLoginButton: 'Login with enterprise account',
|
|
18
|
+
socialLoginTitle: 'Do you want to access another way?',
|
|
19
|
+
emailNotFoundError: 'We couldn\'t find an account for this email',
|
|
20
|
+
},
|
|
21
|
+
pt: {
|
|
22
|
+
welcome: 'Boas vindas à StackSpot AI',
|
|
23
|
+
loginWithEmail: 'Faça login com seu e-mail.',
|
|
24
|
+
loginWithSocialAccount: 'Cadastre-se ou acesse seu teste gratuito com uma conta social',
|
|
25
|
+
label: 'Email corporativo',
|
|
26
|
+
placeholder: 'email@empresa.com',
|
|
27
|
+
continue: 'Continuar',
|
|
28
|
+
or: 'Ou',
|
|
29
|
+
loginWith: 'Entrar com $0',
|
|
30
|
+
emailNotAllowedTitle: '"Este e-mail está vinculado a uma conta Enterprise.',
|
|
31
|
+
emailNotAllowedSubtitle: "Faça login com seu email corporativo.",
|
|
32
|
+
socialLogin: 'Entre ou cadastre-se com uma conta social',
|
|
33
|
+
corporateLoginTitle: 'Já possui uma conta StackSpot Enterprise?',
|
|
34
|
+
corporateLoginButton: 'Entrar na conta Enterprise',
|
|
35
|
+
socialLoginTitle: 'Você quer entrar de outro jeito?',
|
|
36
|
+
emailNotFoundError: 'Não encontramos uma conta para este e-mail.',
|
|
37
|
+
},
|
|
38
|
+
} satisfies Dictionary
|
|
39
|
+
|
|
40
|
+
export const useTranslation = () => useTranslate(dictionary)
|
package/src/hooks.ts
CHANGED
|
@@ -19,9 +19,14 @@ export const useTrialProviders = ({ enabled = true }): [Provider[], boolean] =>
|
|
|
19
19
|
useEffect(() => {
|
|
20
20
|
(async () => {
|
|
21
21
|
if (!SessionManager.instance || !enabled) return
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
try {
|
|
23
|
+
const providers = (await SessionManager.instance.getTrialEnabledProviders()) as Provider[]
|
|
24
|
+
setTrialProviders(providers)
|
|
25
|
+
setIsLoadingTrialProviders(false)
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(error)
|
|
28
|
+
setIsLoadingTrialProviders(false)
|
|
29
|
+
}
|
|
25
30
|
})()
|
|
26
31
|
}, [SessionManager.instance])
|
|
27
32
|
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getCookie } from '@stack-spot/portal-components'
|
|
2
|
+
import { LoginType } from './types'
|
|
3
|
+
|
|
4
|
+
const lastLoginTypeKey = 'lastLoginType'
|
|
5
|
+
const fallbackKeys = ['guided-tour', '@stack-spot/opa:user', 'CHAT_AGENTS', 'RATED_US_IN']
|
|
6
|
+
|
|
7
|
+
export function getLastLoginType(): LoginType {
|
|
8
|
+
const type = localStorage.getItem(lastLoginTypeKey)
|
|
9
|
+
if (type === 'idp' || type === 'sso') return type
|
|
10
|
+
// for now, the user won't have the variable "lastLoginType" set. So, we check for tother variables that may indicate this is an enterprise user
|
|
11
|
+
if (getCookie('stk-session.stackspot.com')) return 'sso'
|
|
12
|
+
for (const key of fallbackKeys) {
|
|
13
|
+
if (localStorage.getItem(key)) return 'sso'
|
|
14
|
+
}
|
|
15
|
+
return 'idp'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function setLastLoginType(type: LoginType) {
|
|
19
|
+
localStorage.setItem(lastLoginTypeKey, type)
|
|
20
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type LoginType = 'sso' | 'idp'
|
|
2
|
+
|
|
3
|
+
interface BaseData {
|
|
4
|
+
type: LoginType,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface SSOData extends BaseData {
|
|
8
|
+
type: 'sso',
|
|
9
|
+
email: string,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface IDPData extends BaseData {
|
|
13
|
+
type: 'idp',
|
|
14
|
+
provider: 'external-idp:github' | 'external-idp:google' | 'external-idp:microsoft',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type LoginData = SSOData | IDPData
|
|
18
|
+
|
|
19
|
+
export type LoginProps = {
|
|
20
|
+
initialValue?: string,
|
|
21
|
+
onSubmit: (data: LoginData) => Promise<void>,
|
|
22
|
+
welcomeText?: string,
|
|
23
|
+
removeLoadingOnSuccess?: boolean,
|
|
24
|
+
className?: string,
|
|
25
|
+
style?: React.CSSProperties,
|
|
26
|
+
banner?: React.ReactNode,
|
|
27
|
+
loginTypes?: LoginType[],
|
|
28
|
+
}
|