move to ui package drectory

This commit is contained in:
Jonathan Summers-Muir committed 2022-09-19 14:00:05 +02:00
1 parent b0233d3e3d
commit 55329d540e
1124 files changed
+888 -2201

No files matched your search

+54
View File
@@ -774,6 +774,60 @@
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/launch-week-5-community-day</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/launch-week-5-one-more-thing</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/pg-jsonschema-a-postgres-extension-for-json-validation</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/postgrest-v10</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/supabase-vault</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/building-a-realtime-trello-board-with-supabase-and-angular</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/launch-week-5-hackathon-winners</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/supabase-beta-update-august-2022</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/blog/choosing-a-postgres-primary-key</loc>
<changefreq>weekly</changefreq>
<changefreq>0.5</changefreq>
</url>
<url>
<loc>https://supabase.com/alternatives/supabase-vs-firebase</loc>
<changefreq>weekly</changefreq>
@@ -1,7 +0,0 @@
.sbui-auth {
width: 100%;
}
.sbui-auth-label {
@apply block text-sm text-gray-700 dark:text-gray-200 font-medium;
}
@@ -1,184 +0,0 @@
import React, { useState } from 'react'
import { Auth } from './'
import { createClient } from '@supabase/supabase-js'
// @ts-ignore
import { Typography, Button, Space } from '../../index'
const supabase = createClient(
'https://rsnibhkhsbfnncjmwnkj.supabase.co',
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYxNTIxNDE1MywiZXhwIjoxOTMwNzkwMTUzfQ.OQEbAaTfgDdLCCht251P2JRD3QDnui6nsU8N-tZA_Mc'
)
export default {
title: 'Auth/Auth',
component: Auth,
}
const Container = (props: any) => {
const { user } = Auth.useUser()
if (user)
return (
<>
<Typography.Text>Signed in: {user.email}</Typography.Text>
<Button block onClick={() => props.supabaseClient.auth.signOut()}>
Sign out
</Button>
</>
)
return props.children
}
export const Default = (args: any) => (
<Auth.UserContextProvider {...args}>
<Container {...args}>
<Auth {...args} />
</Container>
</Auth.UserContextProvider>
)
export const withSocialAuth = (args: any) => (
<Auth.UserContextProvider {...args}>
<Container {...args}>
<Auth {...args} />
</Container>
</Auth.UserContextProvider>
)
export const withAllSocialAuth = (args: any) => (
<Auth.UserContextProvider {...args}>
<Container {...args}>
<Auth {...args} />
</Container>
</Auth.UserContextProvider>
)
export const withSocialLargeButtons = (args: any) => (
<Auth.UserContextProvider {...args}>
<Container {...args}>
<Auth {...args} />
</Container>
</Auth.UserContextProvider>
)
export const withColouredSocialAuth = (args: any) => (
<Auth.UserContextProvider {...args}>
<Container {...args}>
<Auth {...args} />
</Container>
</Auth.UserContextProvider>
)
export const withSocialAuthHorizontal = (args: any) => (
<Auth.UserContextProvider {...args}>
<Container {...args}>
<Auth {...args} />
</Container>
</Auth.UserContextProvider>
)
export const updatePassword = (args: any) => <Auth.UpdatePassword {...args} />
export const ChangeViewState = (args: any) => {
const [view, setView] = useState<
'sign_in' | 'sign_up' | 'forgotten_password' | 'magic_link'
>('sign_in')
return (
<div>
<Space>
<Button
type={view === 'sign_up' ? 'primary' : 'default'}
onClick={() => setView('sign_up')}
>
Sign up
</Button>
<Button
type={view === 'sign_in' ? 'primary' : 'default'}
onClick={() => setView('sign_in')}
>
Sign in
</Button>
<Button
type={view === 'forgotten_password' ? 'primary' : 'default'}
onClick={() => setView('forgotten_password')}
>
Forgotten password
</Button>
<Button
type={view === 'magic_link' ? 'primary' : 'default'}
onClick={() => setView('magic_link')}
>
Magic link
</Button>
</Space>
<Auth.UserContextProvider supabaseClient={supabase}>
<Container supabaseClient={supabase}>
<Auth supabaseClient={supabase} view={view} />
</Container>
</Auth.UserContextProvider>
</div>
)
}
Default.args = {
supabaseClient: supabase,
}
withSocialAuth.args = {
supabaseClient: supabase,
providers: ['facebook', 'google'],
}
withAllSocialAuth.args = {
supabaseClient: supabase,
providers: [
'apple',
'azure',
'bitbucket',
'discord',
'facebook',
'github',
'gitlab',
'google',
'twitch',
'twitter',
],
}
withSocialLargeButtons.args = {
supabaseClient: supabase,
providers: [
'apple',
'azure',
'bitbucket',
'discord',
'facebook',
'github',
'gitlab',
'google',
'twitch',
'twitter',
],
socialButtonSize: 'large',
}
withColouredSocialAuth.args = {
supabaseClient: supabase,
socialColors: true,
providers: [
'apple',
'azure',
'bitbucket',
'discord',
'facebook',
'github',
'gitlab',
'google',
'twitch',
'twitter',
],
}
withSocialAuthHorizontal.args = {
supabaseClient: supabase,
providers: ['facebook', 'google'],
socialLayout: 'horizontal',
}
updatePassword.args = {
supabaseClient: supabase,
}
@@ -1,643 +0,0 @@
import React, { useEffect, useRef, useState } from 'react'
import { SupabaseClient, Provider } from '@supabase/supabase-js'
import {
Input,
Checkbox,
Button,
Space,
Typography,
Divider,
IconKey,
IconMail,
IconInbox,
IconLock,
} from './../../index'
import { UserContextProvider, useUser } from './UserContext'
import * as SocialIcons from './Icons'
// @ts-ignore
import AuthStyles from './Auth.module.css'
const VIEWS: ViewsMap = {
SIGN_IN: 'sign_in',
SIGN_UP: 'sign_up',
FORGOTTEN_PASSWORD: 'forgotten_password',
MAGIC_LINK: 'magic_link',
UPDATE_PASSWORD: 'update_password',
}
interface ViewsMap {
[key: string]: ViewType
}
type ViewType =
| 'sign_in'
| 'sign_up'
| 'forgotten_password'
| 'magic_link'
| 'update_password'
type RedirectTo = undefined | string
export interface Props {
supabaseClient: SupabaseClient
className?: string
children?: React.ReactNode
style?: React.CSSProperties
socialLayout?: 'horizontal' | 'vertical'
socialColors?: boolean
socialButtonSize?: 'tiny' | 'small' | 'medium' | 'large' | 'xlarge'
providers?: Provider[]
verticalSocialLayout?: any
view?: ViewType
redirectTo?: RedirectTo
onlyThirdPartyProviders?: boolean
magicLink?: boolean
}
function Auth({
supabaseClient,
className,
style,
socialLayout = 'vertical',
socialColors = false,
socialButtonSize = 'medium',
providers,
view = 'sign_in',
redirectTo,
onlyThirdPartyProviders = false,
magicLink = false,
}: Props): JSX.Element | null {
const [authView, setAuthView] = useState(view)
const [defaultEmail, setDefaultEmail] = useState('')
const [defaultPassword, setDefaultPassword] = useState('')
const verticalSocialLayout = socialLayout === 'vertical' ? true : false
let containerClasses = [AuthStyles['sbui-auth']]
if (className) {
containerClasses.push(className)
}
const Container = (props: any) => (
<div className={containerClasses.join(' ')} style={style}>
<Space size={8} direction={'vertical'}>
<SocialAuth
supabaseClient={supabaseClient}
verticalSocialLayout={verticalSocialLayout}
providers={providers}
socialLayout={socialLayout}
socialButtonSize={socialButtonSize}
socialColors={socialColors}
redirectTo={redirectTo}
onlyThirdPartyProviders={onlyThirdPartyProviders}
magicLink={magicLink}
/>
{!onlyThirdPartyProviders && props.children}
</Space>
</div>
)
useEffect(() => {
// handle view override
setAuthView(view)
}, [view])
switch (authView) {
case VIEWS.SIGN_IN:
case VIEWS.SIGN_UP:
return (
<Container>
<EmailAuth
id={authView === VIEWS.SIGN_UP ? 'auth-sign-up' : 'auth-sign-in'}
supabaseClient={supabaseClient}
authView={authView}
setAuthView={setAuthView}
defaultEmail={defaultEmail}
defaultPassword={defaultPassword}
setDefaultEmail={setDefaultEmail}
setDefaultPassword={setDefaultPassword}
redirectTo={redirectTo}
magicLink={magicLink}
/>
</Container>
)
case VIEWS.FORGOTTEN_PASSWORD:
return (
<Container>
<ForgottenPassword
supabaseClient={supabaseClient}
setAuthView={setAuthView}
redirectTo={redirectTo}
/>
</Container>
)
case VIEWS.MAGIC_LINK:
return (
<Container>
<MagicLink
supabaseClient={supabaseClient}
setAuthView={setAuthView}
redirectTo={redirectTo}
/>
</Container>
)
case VIEWS.UPDATE_PASSWORD:
return (
<Container>
<UpdatePassword supabaseClient={supabaseClient} />
</Container>
)
default:
return null
}
}
function SocialAuth({
className,
style,
supabaseClient,
children,
socialLayout = 'vertical',
socialColors = false,
socialButtonSize,
providers,
verticalSocialLayout,
redirectTo,
onlyThirdPartyProviders,
magicLink,
...props
}: Props) {
const buttonStyles: any = {
azure: {
backgroundColor: '#008AD7',
color: 'white',
},
bitbucket: {
backgroundColor: '#205081',
color: 'white',
},
facebook: {
backgroundColor: '#4267B2',
color: 'white',
},
github: {
backgroundColor: '#333',
color: 'white',
},
gitlab: {
backgroundColor: '#FC6D27',
},
google: {
backgroundColor: '#ce4430',
color: 'white',
},
twitter: {
backgroundColor: '#1DA1F2',
color: 'white',
},
apple: {
backgroundColor: '#000',
color: 'white',
},
discord: {
backgroundColor: '#404fec',
color: 'white',
},
twitch: {
backgroundColor: '#9146ff',
color: 'white',
},
}
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const handleProviderSignIn = async (provider: Provider) => {
setLoading(true)
const { error } = await supabaseClient.auth.signIn(
{ provider },
{ redirectTo }
)
if (error) setError(error.message)
setLoading(false)
}
return (
<Space size={8} direction={'vertical'}>
{providers && providers.length > 0 && (
<React.Fragment>
<Space size={4} direction={'vertical'}>
<Typography.Text
type="secondary"
className={AuthStyles['sbui-auth-label']}
>
Sign in with
</Typography.Text>
<Space size={2} direction={socialLayout}>
{providers.map((provider) => {
// @ts-ignore
const AuthIcon = SocialIcons[provider]
return (
<div
key={provider}
style={!verticalSocialLayout ? { flexGrow: 1 } : {}}
>
<Button
block
type="default"
shadow
size={socialButtonSize}
style={socialColors ? buttonStyles[provider] : {}}
icon={<AuthIcon />}
loading={loading}
onClick={() => handleProviderSignIn(provider)}
className="flex items-center"
>
{verticalSocialLayout && 'Sign up with ' + provider}
</Button>
</div>
)
})}
</Space>
</Space>
{!onlyThirdPartyProviders && <Divider>or continue with</Divider>}
</React.Fragment>
)}
</Space>
)
}
function EmailAuth({
authView,
defaultEmail,
defaultPassword,
id,
setAuthView,
setDefaultEmail,
setDefaultPassword,
supabaseClient,
redirectTo,
magicLink,
}: {
authView: ViewType
defaultEmail: string
defaultPassword: string
id: 'auth-sign-up' | 'auth-sign-in'
setAuthView: any
setDefaultEmail: (email: string) => void
setDefaultPassword: (password: string) => void
supabaseClient: SupabaseClient
redirectTo?: RedirectTo
magicLink?: boolean
}) {
const isMounted = useRef<boolean>(true)
const [email, setEmail] = useState(defaultEmail)
const [password, setPassword] = useState(defaultPassword)
const [rememberMe, setRememberMe] = useState(false)
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const [message, setMessage] = useState('')
useEffect(() => {
setEmail(defaultEmail)
setPassword(defaultPassword)
return () => {
isMounted.current = false
}
}, [authView])
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setError('')
setLoading(true)
switch (authView) {
case 'sign_in':
const { error: signInError } = await supabaseClient.auth.signIn(
{
email,
password,
},
{ redirectTo }
)
if (signInError) setError(signInError.message)
break
case 'sign_up':
const { error: signUpError, data: signUpData } =
await supabaseClient.auth.signUp(
{
email,
password,
},
{ redirectTo }
)
if (signUpError) setError(signUpError.message)
// checking if it has access_token to know if email verification is disabled
else if (signUpData?.hasOwnProperty('confirmation_sent_at'))
setMessage('Check your email for the confirmation link.')
break
}
/*
* it is possible the auth component may have been unmounted at this point
* check if component is mounted before setting a useState
*/
if (isMounted.current) setLoading(false)
}
const handleViewChange = (newView: ViewType) => {
setDefaultEmail(email)
setDefaultPassword(password)
setAuthView(newView)
}
return (
<form id={id} onSubmit={handleSubmit}>
<Space size={6} direction={'vertical'}>
<Space size={3} direction={'vertical'}>
<Input
label="Email address"
autoComplete="email"
defaultValue={email}
icon={<IconMail size={21} stroke={'#666666'} />}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setEmail(e.target.value)
}
/>
<Input
label="Password"
type="password"
defaultValue={password}
autoComplete="current-password"
icon={<IconKey size={21} stroke={'#666666'} />}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setPassword(e.target.value)
}
/>
</Space>
<Space direction="vertical" size={6}>
<Space style={{ justifyContent: 'space-between' }}>
<Checkbox
label="Remember me"
name="remember_me"
id="remember_me"
onChange={(value: React.ChangeEvent<HTMLInputElement>) =>
setRememberMe(value.target.checked)
}
/>
{authView === VIEWS.SIGN_IN && (
<Typography.Link
href="#auth-forgot-password"
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault()
setAuthView(VIEWS.FORGOTTEN_PASSWORD)
}}
>
Forgot your password?
</Typography.Link>
)}
</Space>
<Button
htmlType="submit"
type="primary"
size="large"
icon={<IconLock size={21} />}
loading={loading}
block
>
{authView === VIEWS.SIGN_IN ? 'Sign in' : 'Sign up'}
</Button>
</Space>
<Space direction="vertical" style={{ textAlign: 'center' }}>
{authView === VIEWS.SIGN_IN && magicLink && (
<Typography.Link
href="#auth-magic-link"
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault()
setAuthView(VIEWS.MAGIC_LINK)
}}
>
Sign in with magic link
</Typography.Link>
)}
{authView === VIEWS.SIGN_IN ? (
<Typography.Link
href="#auth-sign-up"
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault()
handleViewChange(VIEWS.SIGN_UP)
}}
>
Don't have an account? Sign up
</Typography.Link>
) : (
<Typography.Link
href="#auth-sign-in"
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault()
handleViewChange(VIEWS.SIGN_IN)
}}
>
Do you have an account? Sign in
</Typography.Link>
)}
{message && <Typography.Text>{message}</Typography.Text>}
{error && <Typography.Text type="danger">{error}</Typography.Text>}
</Space>
</Space>
</form>
)
}
function MagicLink({
setAuthView,
supabaseClient,
redirectTo,
}: {
setAuthView: any
supabaseClient: SupabaseClient
redirectTo?: RedirectTo
}) {
const [email, setEmail] = useState('')
const [error, setError] = useState('')
const [message, setMessage] = useState('')
const [loading, setLoading] = useState(false)
const handleMagicLinkSignIn = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setError('')
setMessage('')
setLoading(true)
const { error } = await supabaseClient.auth.signIn(
{ email },
{ redirectTo }
)
if (error) setError(error.message)
else setMessage('Check your email for the magic link')
setLoading(false)
}
return (
<form id="auth-magic-link" onSubmit={handleMagicLinkSignIn}>
<Space size={4} direction={'vertical'}>
<Space size={3} direction={'vertical'}>
<Input
label="Email address"
placeholder="Your email address"
icon={<IconMail size={21} stroke={'#666666'} />}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setEmail(e.target.value)
}
/>
<Button
block
size="large"
htmlType="submit"
icon={<IconInbox size={21} />}
loading={loading}
>
Send magic link
</Button>
</Space>
<Typography.Link
href="#auth-sign-in"
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault()
setAuthView(VIEWS.SIGN_IN)
}}
>
Sign in with password
</Typography.Link>
{message && <Typography.Text>{message}</Typography.Text>}
{error && <Typography.Text type="danger">{error}</Typography.Text>}
</Space>
</form>
)
}
function ForgottenPassword({
setAuthView,
supabaseClient,
redirectTo,
}: {
setAuthView: any
supabaseClient: SupabaseClient
redirectTo?: RedirectTo
}) {
const [email, setEmail] = useState('')
const [error, setError] = useState('')
const [message, setMessage] = useState('')
const [loading, setLoading] = useState(false)
const handlePasswordReset = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setError('')
setMessage('')
setLoading(true)
const { error } = await supabaseClient.auth.api.resetPasswordForEmail(
email,
{ redirectTo }
)
if (error) setError(error.message)
else setMessage('Check your email for the password reset link')
setLoading(false)
}
return (
<form id="auth-forgot-password" onSubmit={handlePasswordReset}>
<Space size={4} direction={'vertical'}>
<Space size={3} direction={'vertical'}>
<Input
label="Email address"
placeholder="Your email address"
icon={<IconMail size={21} stroke={'#666666'} />}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setEmail(e.target.value)
}
/>
<Button
block
size="large"
htmlType="submit"
icon={<IconInbox size={21} />}
loading={loading}
>
Send reset password instructions
</Button>
</Space>
<Typography.Link
href="#auth-sign-in"
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault()
setAuthView(VIEWS.SIGN_IN)
}}
>
Go back to sign in
</Typography.Link>
{message && <Typography.Text>{message}</Typography.Text>}
{error && <Typography.Text type="danger">{error}</Typography.Text>}
</Space>
</form>
)
}
function UpdatePassword({
supabaseClient,
}: {
supabaseClient: SupabaseClient
}) {
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [message, setMessage] = useState('')
const [loading, setLoading] = useState(false)
const handlePasswordReset = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setError('')
setMessage('')
setLoading(true)
const { error } = await supabaseClient.auth.update({ password })
if (error) setError(error.message)
else setMessage('Your password has been updated')
setLoading(false)
}
return (
<form id="auth-update-password" onSubmit={handlePasswordReset}>
<Space size={4} direction={'vertical'}>
<Space size={3} direction={'vertical'}>
<Input
label="New password"
placeholder="Enter your new password"
type="password"
icon={<IconKey size={21} stroke={'#666666'} />}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setPassword(e.target.value)
}
/>
<Button
block
size="large"
htmlType="submit"
icon={<IconKey size={21} />}
loading={loading}
>
Update password
</Button>
</Space>
{message && <Typography.Text>{message}</Typography.Text>}
{error && <Typography.Text type="danger">{error}</Typography.Text>}
</Space>
</form>
)
}
Auth.ForgottenPassword = ForgottenPassword
Auth.UpdatePassword = UpdatePassword
Auth.MagicLink = MagicLink
Auth.UserContextProvider = UserContextProvider
Auth.useUser = useUser
export default Auth
@@ -1,236 +0,0 @@
import React from 'react'
const size = 21
export const google = () => (
<svg
width={size}
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="google"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 488 512"
>
<path
fill="currentColor"
d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"
></path>
</svg>
)
export const facebook = () => (
<svg
width={size}
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="facebook-square"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
>
<path
fill="currentColor"
d="M400 32H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h137.25V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.27c-30.81 0-40.42 19.12-40.42 38.73V256h68.78l-11 71.69h-57.78V480H400a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48z"
></path>
</svg>
)
export const twitter = () => (
<svg
width={size}
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="twitter"
className="svg-inline--fa fa-twitter fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="currentColor"
d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"
></path>
</svg>
)
export const apple = () => (
<svg
width={size}
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="apple"
className="svg-inline--fa fa-apple fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="currentColor"
d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z"
></path>
</svg>
)
export const github = () => (
<svg
width={size}
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="github"
className="svg-inline--fa fa-github fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 496 512"
>
<path
fill="currentColor"
d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"
></path>
</svg>
)
export const gitlab = () => (
<svg
width={size}
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="gitlab"
className="svg-inline--fa fa-gitlab fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="currentColor"
d="M105.2 24.9c-3.1-8.9-15.7-8.9-18.9 0L29.8 199.7h132c-.1 0-56.6-174.8-56.6-174.8zM.9 287.7c-2.6 8 .3 16.9 7.1 22l247.9 184-226.2-294zm160.8-88l94.3 294 94.3-294zm349.4 88l-28.8-88-226.3 294 247.9-184c6.9-5.1 9.7-14 7.2-22zM425.7 24.9c-3.1-8.9-15.7-8.9-18.9 0l-56.6 174.8h132z"
></path>
</svg>
)
export const bitbucket = () => (
<svg
width={size}
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="bitbucket"
className="svg-inline--fa fa-bitbucket fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="currentColor"
d="M22.2 32A16 16 0 0 0 6 47.8a26.35 26.35 0 0 0 .2 2.8l67.9 412.1a21.77 21.77 0 0 0 21.3 18.2h325.7a16 16 0 0 0 16-13.4L505 50.7a16 16 0 0 0-13.2-18.3 24.58 24.58 0 0 0-2.8-.2L22.2 32zm285.9 297.8h-104l-28.1-147h157.3l-25.2 147z"
></path>
</svg>
)
export const discord = () => (
<svg
width={size}
height={size}
viewBox="0 0 71 55"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0)">
<path
d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z"
fill="currentColor"
/>
</g>
</svg>
)
export const azure = () => (
<svg
width={size}
height={size}
viewBox="0 0 96 96"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient
id="e399c19f-b68f-429d-b176-18c2117ff73c"
x1="-1032.172"
x2="-1059.213"
y1="145.312"
y2="65.426"
gradientTransform="matrix(1 0 0 -1 1075 158)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#fff"></stop>
<stop offset="1" stopColor="#fff"></stop>
</linearGradient>
<linearGradient
id="ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15"
x1="-1023.725"
x2="-1029.98"
y1="108.083"
y2="105.968"
gradientTransform="matrix(1 0 0 -1 1075 158)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopOpacity=".3"></stop>
<stop offset=".071" stopOpacity=".2"></stop>
<stop offset=".321" stopOpacity=".1"></stop>
<stop offset=".623" stopOpacity=".05"></stop>
<stop offset="1" stopOpacity="0"></stop>
</linearGradient>
<linearGradient
id="a7fee970-a784-4bb1-af8d-63d18e5f7db9"
x1="-1027.165"
x2="-997.482"
y1="147.642"
y2="68.561"
gradientTransform="matrix(1 0 0 -1 1075 158)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#fff"></stop>
<stop offset="1" stopColor="#fff"></stop>
</linearGradient>
</defs>
<path
fill="url(#e399c19f-b68f-429d-b176-18c2117ff73c)"
d="M33.338 6.544h26.038l-27.03 80.087a4.152 4.152 0 0 1-3.933 2.824H8.149a4.145 4.145 0 0 1-3.928-5.47L29.404 9.368a4.152 4.152 0 0 1 3.934-2.825z"
></path>
<path
fill="currentColor"
d="M71.175 60.261h-41.29a1.911 1.911 0 0 0-1.305 3.309l26.532 24.764a4.171 4.171 0 0 0 2.846 1.121h23.38z"
></path>
<path
fill="currentColor"
d="M33.338 6.544a4.118 4.118 0 0 0-3.943 2.879L4.252 83.917a4.14 4.14 0 0 0 3.908 5.538h20.787a4.443 4.443 0 0 0 3.41-2.9l5.014-14.777 17.91 16.705a4.237 4.237 0 0 0 2.666.972H81.24L71.024 60.261l-29.781.007L59.47 6.544z"
></path>
<path
fill="currentColor"
d="M66.595 9.364a4.145 4.145 0 0 0-3.928-2.82H33.648a4.146 4.146 0 0 1 3.928 2.82l25.184 74.62a4.146 4.146 0 0 1-3.928 5.472h29.02a4.146 4.146 0 0 0 3.927-5.472z"
></path>
</svg>
)
export const twitch = () => (
<svg
width={size}
aria-hidden="true"
focusable="false"
data-prefix="fab"
data-icon="twitch"
className="svg-inline--fa fa-twitch fa-w-16"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="currentColor"
d="M391.17,103.47H352.54v109.7h38.63ZM285,103H246.37V212.75H285ZM120.83,0,24.31,91.42V420.58H140.14V512l96.53-91.42h77.25L487.69,256V0ZM449.07,237.75l-77.22,73.12H294.61l-67.6,64v-64H140.14V36.58H449.07Z"
></path>
</svg>
)
@@ -1,50 +0,0 @@
import React, { useEffect, useState, createContext, useContext } from 'react'
import { SupabaseClient, Session, User } from '@supabase/supabase-js'
export interface AuthSession {
user: User | null
session: Session | null
}
const UserContext = createContext<AuthSession>({ user: null, session: null })
export interface Props {
supabaseClient: SupabaseClient
[propName: string]: any
}
export const UserContextProvider = (props: Props) => {
const { supabaseClient } = props
const [session, setSession] = useState<Session | null>(
supabaseClient.auth.session()
)
const [user, setUser] = useState<User | null>(session?.user ?? null)
useEffect(() => {
const { data: authListener } = supabaseClient.auth.onAuthStateChange(
async (event, session) => {
setSession(session)
setUser(session?.user ?? null)
}
)
return () => {
authListener?.unsubscribe()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
const value = {
session,
user,
}
return <UserContext.Provider value={value} {...props} />
}
export const useUser = () => {
const context = useContext(UserContext)
if (context === undefined) {
throw new Error(`useUser must be used within a UserContextProvider.`)
}
return context
}
@@ -1 +0,0 @@
export { default as Auth } from './Auth'
@@ -1,13 +0,0 @@
// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import React from 'react'
// @ts-ignore
import { Activity } from 'react-feather'
import IconBase from './../../IconBase'
function IconActivity(props: any) {
return <IconBase icon={Activity} {...props} />
}
export default IconActivity
-5
View File
@@ -1,5 +0,0 @@
{
"extends": "tsconfig/react-library.json",
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}
File renamed without changes.
File renamed without changes.
@@ -1,3 +1,5 @@
import * as React from 'react'
// // GENERAL
export * from './src/components/Button'
@@ -13,6 +15,7 @@ export * from './src/components/Tabs'
export * from './src/components/Alert'
export * from './src/components/Accordion'
export * from './src/components/Collapsible'
// export * from './src/components/Breadcrumb'
// NAV
@@ -208,7 +211,7 @@ export * from './src/components/Icon/icons/IconLink'
export * from './src/components/Icon/icons/IconLinkedin'
export * from './src/components/Icon/icons/IconList'
export * from './src/components/Icon/icons/IconLoader'
export * from './src/components/Icon/icons/IconLoader'
export * from './src/components/Icon/icons/IconLock'
export * from './src/components/Icon/icons/IconLogIn'
export * from './src/components/Icon/icons/IconLogOut'
export * from './src/components/Icon/icons/IconMail'
File renamed without changes.
File renamed without changes.
@@ -31,7 +31,7 @@ Object.values(icons).map((icon) => {
`${dir}/Icon${icon.name}.tsx`,
`// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import React from 'react'
import * as React from 'react'
// @ts-ignore
import { ${icon.name} } from 'react-feather'
import IconBase from './../../IconBase'
@@ -1,5 +1,5 @@
{
"name": "common2",
"name": "ui",
"version": "0.0.0",
"main": "./index.tsx",
"types": "./index.tsx",
@@ -27,14 +27,13 @@
"lodash": "^4.17.20",
"postcss": "^8.4.5",
"prop-types": "^15.7.2",
"react-feather": "^2.0.10",
"tailwindcss": "^3.0.15",
"tailwindcss-radix": "^1.6.0"
},
"devDependencies": {
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"config": "*",
"tsconfig": "*",
"typescript": "^4.5.3"
"typescript": "4.3.4"
}
}
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,6 @@
import React from 'react'
import { Alert } from './../../index'
import { Alert } from './../../../'
// import ThemeProvider from '../../theme/themeContext'
@@ -11,78 +11,67 @@ export default {
export const Neutral = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const Success = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const SuccessWithIcon = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const Danger = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const DangerWithIcon = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const Info = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const InfoWithIcon = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const Warning = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const WarningWithIcon = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const Closable = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
export const WithAction = (args: any) => (
<Alert {...args}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet
labore.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore.
</Alert>
)
@@ -1,16 +1,12 @@
import React from 'react'
import { Breadcrumb } from '../Breadcrumb'
import { Textarea } from '../Textarea'
import Typography from '../Typography'
import { Breadcrumb } from './../../../'
export default {
title: 'Navigation/Breadcrumb',
component: Breadcrumb,
}
export const Default = ({ children, ...args }) => (
<Breadcrumb {...args}>{children}</Breadcrumb>
)
export const Default = ({ children, ...args }: any) => <Breadcrumb {...args}>{children}</Breadcrumb>
Default.args = {
children: [
@@ -9,12 +9,7 @@ interface Props {
spacing?: 'small' | 'medium' | 'large'
}
const Breadcrumb = ({
className,
style,
children,
spacing = 'small',
}: Props) => {
const Breadcrumb = ({ className, style, children, spacing = 'small' }: Props) => {
let classes = [BreadcrumbStyle['sbui-breadcrumb--container']]
let seperatorClasses = [BreadcrumbStyle['sbui-breadcrumb--separator']]
@@ -32,11 +27,7 @@ const Breadcrumb = ({
<li className={BreadcrumbStyle['sbui-breadcrumb--item-container']}>
{child}
{idx + 1 < children!.length && (
<IconChevronRight
size={12}
className={seperatorClasses.join(' ')}
aria-hidden="true"
/>
<IconChevronRight size={12} className={seperatorClasses.join(' ')} aria-hidden="true" />
)}
</li>
))}
@@ -119,7 +119,7 @@ export const Button = forwardRef<RefHandle, ButtonProps>(
const iconLoaderClasses = [__styles.loading]
// custom button tag
const CustomButton: React.FC<CustomButtonProps> = ({ ...props }) => {
const CustomButton = ({ ...props }) => {
const Tag = as as keyof JSX.IntrinsicElements
return <Tag {...props} />
}
@@ -9,8 +9,7 @@ import defaultTheme from '../../lib/theme/defaultTheme'
import { useFormContext } from '../Form/FormContext'
import styleHandler from '../../lib/theme/styleHandler'
export interface InputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
afterLabel?: string
beforeLabel?: string
description?: string
@@ -106,7 +105,6 @@ export function Checkbox({
checked,
value,
onChange,
onFocus,
onBlur,
size = 'medium',
disabled = false,
@@ -170,10 +168,8 @@ export function Checkbox({
id={markupId}
name={markupName}
type="checkbox"
// className={CheckboxStyles['sbui-checkbox']}
className={[__styles.base, __styles.size[size]].join(' ')}
onChange={onInputChange}
onFocus={onFocus ? (event) => onFocus(event) : undefined}
onBlur={handleBlurEvent}
checked={active}
value={value ? value : markupId}
@@ -188,10 +184,7 @@ export function Checkbox({
<span>
{beforeLabel && (
<span
className={[
__styles.label_before.base,
__styles.label_before[size],
].join(' ')}
className={[__styles.label_before.base, __styles.label_before[size]].join(' ')}
>
{beforeLabel}
</span>
@@ -199,10 +192,7 @@ export function Checkbox({
{label}
{afterLabel && (
<span
className={[
__styles.label_after.base,
__styles.label_after[size],
].join(' ')}
className={[__styles.label_after.base, __styles.label_after[size]].join(' ')}
>
{afterLabel}
</span>
@@ -210,12 +200,7 @@ export function Checkbox({
</span>
{description && (
<p
className={[
__styles.description.base,
__styles.description[size],
].join(' ')}
>
<p className={[__styles.description.base, __styles.description[size]].join(' ')}>
{description}
</p>
)}
@@ -1,7 +1,5 @@
import React from 'react'
import * as RadixCollapsible from '@radix-ui/react-collapsible'
import { Button } from '../Button'
import { IconChevronRight, IconGlobe } from '../..'
import React from 'react'
import styleHandler from '../../lib/theme/styleHandler'
export interface CollapsibleProps extends RadixCollapsible.CollapsibleProps {
@@ -34,18 +32,8 @@ export const Collapsible = ({
)
}
export function Trigger({
children,
asChild,
}: {
children: React.ReactNode
asChild?: boolean
}) {
return (
<RadixCollapsible.Trigger asChild={asChild}>
{children}
</RadixCollapsible.Trigger>
)
export function Trigger({ children, asChild }: { children: React.ReactNode; asChild?: boolean }) {
return <RadixCollapsible.Trigger asChild={asChild}>{children}</RadixCollapsible.Trigger>
}
export function Content({
@@ -57,9 +45,7 @@ export function Content({
}) {
const __styles = styleHandler('collapsible')
return (
<RadixCollapsible.Content
className={[__styles.content, className].join(' ')}
>
<RadixCollapsible.Content className={[__styles.content, className].join(' ')}>
{children}
</RadixCollapsible.Content>
)
@@ -1,30 +1,23 @@
import React, { useEffect, useReducer, useState } from 'react'
import { Form } from './../'
import React, { useReducer, useState } from 'react'
import {
Input,
Button,
InputNumber,
Toggle,
Checkbox,
Radio,
Select,
Listbox,
IconChevronUp,
IconCheckCircle,
IconCheck,
IconArrowRight,
IconX,
Tabs,
IconCheck,
IconChevronUp,
IconGlobe,
IconTrash,
IconAlertCircle,
} from '../../../index'
IconX,
Input,
InputNumber,
Tabs,
Toggle,
} from '../../../../'
import { Form } from './../'
import * as Yup from 'yup'
import { Collapsible } from '../../Collapsible'
import AuthFormSchema from './AuthFormSchema.json'
import { Modal } from '../../Modal'
import AuthFormSchema from './AuthFormSchema.json'
// import GoogleIcon './../../../static/icons/google-icon.svg'
@@ -32,36 +25,33 @@ const DocsButton = () => {
return (
<button
className="
group
w-full
bg-scale-300
dark:bg-scale-100
border
border-scale-500 hover:border-scale-700
dark:border-scale-300 dark:hover:border-scale-500
px-6 py-4
my-6
flex gap-6 items-center
rounded-md shadow-sm
bg-scale-300
dark:bg-scale-100
border-scale-500
hover:border-scale-700
dark:border-scale-300
dark:hover:border-scale-500 group
my-6 flex
w-full items-center
gap-6
rounded-md border px-6
py-4 text-left
shadow-sm
transition
text-left
"
>
<div className="transition duration-500 bg-brand-900 rounded p-1 w-10 h-10 group-hover:-rotate-3 group-hover:scale-110">
<div className="bg-brand-900 h-10 w-10 rounded p-1 transition duration-500 group-hover:-rotate-3 group-hover:scale-110">
{/* <img
className="text-white w-12 h-12"
src="/icons/docs-illustration.svg"
/> */}
</div>
<div className="grow">
<h3 className="text-scale-1200">
Use authentication as the backbone of your app
</h3>
<h3 className="text-scale-1200">Use authentication as the backbone of your app</h3>
<p className="text-scale-900 text-xs">
Learn how to use Supabase Auth with Auth Policies to pick and choose
what users see what.
Learn how to use Supabase Auth with Auth Policies to pick and choose what users see what.
</p>
<p className="text-brand-900 mt-2 text-xs flex items-center gap-1">
<p className="text-brand-900 mt-2 flex items-center gap-1 text-xs">
See Documenation
<span className="transition group-hover:translate-x-1">
<IconArrowRight strokeWidth={2} width={12} />
@@ -91,10 +81,10 @@ const CardForm = () => {
bg-scale-100
dark:bg-scale-300
border border-scale-400 rounded-md my-8 shadow overflow-hidden"
border-scale-400 my-8 overflow-hidden rounded-md border shadow"
>
{header && (
<div className="bg-scale-100 dark:bg-scale-200 px-8 py-4 border-b border-scale-400">
<div className="bg-scale-100 dark:bg-scale-200 border-scale-400 border-b px-8 py-4">
{header}
</div>
)}
@@ -146,13 +136,9 @@ const CardForm = () => {
<Panel
footer={
<>
<div className="border-t border-scale-400"></div>
<div className="py-3 px-6 flex gap-2 justify-end">
<Button
type="default"
htmlType="reset"
onClick={() => handleReset()}
>
<div className="border-scale-400 border-t"></div>
<div className="flex justify-end gap-2 py-3 px-6">
<Button type="default" htmlType="reset" onClick={() => handleReset()}>
Cancel
</Button>
<Button
@@ -168,11 +154,7 @@ const CardForm = () => {
}
>
<Section
header={
<label className="text-sm text-scale-1200 col-span-4">
User Signups
</label>
}
header={<label className="text-scale-1200 col-span-4 text-sm">User Signups</label>}
>
<Toggle
id="enable_signups"
@@ -182,13 +164,9 @@ const CardForm = () => {
descriptionText="If this is disabled, new users will not be able to sign up to your application."
/>
</Section>
<div className="border-t border-scale-400"></div>
<div className="border-scale-400 border-t"></div>
<Section
header={
<label className="text-sm text-scale-1200 col-span-4">
User Sessions
</label>
}
header={<label className="text-scale-1200 col-span-4 text-sm">User Sessions</label>}
>
<InputNumber
className="col-span-8"
@@ -208,8 +186,7 @@ const CardForm = () => {
let initialState: any = {}
AuthFormSchema.map((provider) => {
initialState[`${provider.name.toLowerCase()}-active`] =
provider.name == 'Email' ? true : false
initialState[`${provider.name.toLowerCase()}-active`] = provider.name == 'Email' ? true : false
initialState[`${provider.name.toLowerCase()}-app-id`] = ''
initialState[`${provider.name.toLowerCase()}-id-key`] = ''
})
@@ -228,10 +205,7 @@ function providersReducer(state, action) {
}
const AuthForm = () => {
const [providersFormState, dispatchProvidersForm] = useReducer(
providersReducer,
initialState
)
const [providersFormState, dispatchProvidersForm] = useReducer(providersReducer, initialState)
console.log('providersFormState from useState', providersFormState)
@@ -245,9 +219,9 @@ const AuthForm = () => {
header?: React.ReactNode
footer?: React.ReactNode
}) => (
<div className="bg-scale-300 border border-scale-400 rounded-md w-3/4 mx-auto my-8 shadow overflow-hidden">
<div className="bg-scale-300 border-scale-400 mx-auto my-8 w-3/4 overflow-hidden rounded-md border shadow">
{header && (
<div className="bg-scale-100 dark:bg-scale-200 px-8 py-4 border-b border-scale-400">
<div className="bg-scale-100 dark:bg-scale-200 border-scale-400 border-b px-8 py-4">
{header}
</div>
)}
@@ -279,12 +253,12 @@ const AuthForm = () => {
return (
<div
className="
bg-scale-400 border border-scale-500 mx-auto shadow overflow-hidden
first:rounded-tr first:rounded-tl
last:rounded-br last:rounded-bl
hover:border-scale-700
hover:bg-scale-500
transition
bg-scale-400 border-scale-500 hover:border-scale-700 hover:bg-scale-500 mx-auto overflow-hidden
border shadow
transition first:rounded-tr
first:rounded-tl
last:rounded-br
last:rounded-bl
"
>
{children}
@@ -322,19 +296,19 @@ const AuthForm = () => {
bg-scale-100 dark:bg-scale-300
hover:bg-scale-200 dark:hover:bg-scale-500
data-open:bg-scale-200 dark:data-open:bg-scale-500
border
border-scale-300 dark:border-scale-500
hover:border-scale-500 dark:hover:border-scale-700
border-scale-300
dark:border-scale-500 hover:border-scale-500
dark:hover:border-scale-700 data-open:border-scale-700
mx-auto shadow overflow-hidden
first:rounded-tr first:rounded-tl
last:rounded-br last:rounded-bl
data-open:pb-px col-span-12 mx-auto
-space-y-px overflow-hidden
border shadow
data-open:border-scale-700
data-open:pb-px
transition
-space-y-px col-span-12
first:rounded-tr
first:rounded-tl
last:rounded-br last:rounded-bl
hover:z-50
"
>
@@ -343,13 +317,13 @@ const AuthForm = () => {
type="button"
className="
group text-scale-1200
text-scale-1200 group
flex
justify-between items-center w-full rounded p-3 px-6"
w-full items-center justify-between rounded p-3 px-6"
>
<div className="flex gap-3 items-center">
<div className="flex items-center gap-3">
<IconChevronUp
className="text-scale-800 transition data-open-parent:rotate-0 data-closed-parent:rotate-180"
className="text-scale-800 data-open-parent:rotate-0 data-closed-parent:rotate-180 transition"
strokeWidth={2}
/>
@@ -362,16 +336,16 @@ const AuthForm = () => {
<span className="text-base">{provider.name}</span>
</div>
<div className="flex gap-3 items-center">
<div className="flex items-center gap-3">
{active ? (
<div className="bg-brand-200 border border-brand-700 py-1 px-1 text-brand-900 text-xs rounded-full flex gap-1 items-center">
<span className="text-xs bg-brand-900 rounded-full p-0.5 text-brand-200">
<div className="bg-brand-200 border-brand-700 text-brand-900 flex items-center gap-1 rounded-full border py-1 px-1 text-xs">
<span className="bg-brand-900 text-brand-200 rounded-full p-0.5 text-xs">
<IconCheck strokeWidth={2} size={12} />
</span>
<span className="px-1">Enabled</span>
</div>
) : (
<div className="bg-scale-100 dark:bg-scale-300 border border-scale-500 dark:border-scale-700 py-1 px-3 text-scale-900 text-xs rounded-md">
<div className="bg-scale-100 dark:bg-scale-300 border-scale-500 dark:border-scale-700 text-scale-900 rounded-md border py-1 px-3 text-xs">
Disabled
</div>
)}
@@ -400,11 +374,9 @@ const AuthForm = () => {
const input_id_key = `${provider.name.toLowerCase()}-id-key`
if (values[input_app_id] && values[input_app_id].length < 10)
errors[input_app_id] =
'App ID must be at least 10 characters long'
errors[input_app_id] = 'App ID must be at least 10 characters long'
if (values[input_app_id] && values[input_id_key].length < 10)
errors[input_id_key] =
'Secret must be at least 10 characters long'
errors[input_id_key] = 'Secret must be at least 10 characters long'
if (values[input_active]) {
if (!values[input_app_id])
@@ -432,17 +404,17 @@ const AuthForm = () => {
<div
className="
bg-scale-100 dark:bg-scale-300
group text-scale-1200 border-t border-scale-500 py-6 px-6
text-scale-1200 border-scale-500 group border-t py-6 px-6
"
>
<div className="space-y-6 max-w-md mx-auto">
<div className="mx-auto max-w-md space-y-6">
<Toggle
id={`${provider.name.toLowerCase()}-active`}
name={`${provider.name.toLowerCase()}-active`}
label={`Enable ${provider.name} Provider`}
layout="flex"
/>
<div className="border-t border-scale-400"></div>
<div className="border-scale-400 border-t"></div>
{provider.form.map((x) => {
return (
<Input
@@ -455,8 +427,8 @@ const AuthForm = () => {
/>
)
})}
<div className="border-t border-scale-400"></div>
<div className="flex items-center gap-3 justify-end">
<div className="border-scale-400 border-t"></div>
<div className="flex items-center justify-end gap-3">
<Button
htmlType="reset"
onClick={() => {
@@ -493,10 +465,7 @@ const AuthForm = () => {
}
// type to do later..
let domainInitialState: any = [
'https://summersmuir.com',
'http://localhost:3000',
]
let domainInitialState: any = ['https://summersmuir.com', 'http://localhost:3000']
// AuthFormSchema.map((provider) => {
// domainInitialState[`${provider.name.toLowerCase()}-active`] =
@@ -548,10 +517,7 @@ function domainsReducer(state, action) {
}
const DomainsForm = () => {
const [domainsFormState, dispatchDomainsForm] = useReducer(
domainsReducer,
domainInitialState
)
const [domainsFormState, dispatchDomainsForm] = useReducer(domainsReducer, domainInitialState)
const [deleteOpen, setDeleteOpen] = useState(false)
const [deleteLoading, setDeleteLoading] = useState(false)
@@ -582,18 +548,14 @@ const DomainsForm = () => {
return (
<>
<div className="flex items-center justify-between my-6">
<div className="my-6 flex items-center justify-between">
<div>
<h3 className="text-scale-1200 text-2xl mb-2">
Authorised domains
</h3>
<p className="text-sm text-scale-900">
Turn payment methods on and off in one click – no engineering time
required.
<h3 className="text-scale-1200 mb-2 text-2xl">Authorised domains</h3>
<p className="text-scale-900 text-sm">
Turn payment methods on and off in one click – no engineering time required.
</p>
<p className="text-sm text-scale-900">
Use our guide to check which payment methods are compatible with
your integration.
<p className="text-scale-900 text-sm">
Use our guide to check which payment methods are compatible with your integration.
</p>
</div>
<Button onClick={() => setOpen(true)}>Add domain</Button>
@@ -602,7 +564,7 @@ const DomainsForm = () => {
visible={open}
onCancel={() => setOpen(!open)}
header={
<div className="flex items-center gap-2 text-scale-1200">
<div className="text-scale-1200 flex items-center gap-2">
<div className="flex items-baseline gap-2">
<h3 className="text-sm">Add a new domain</h3>
</div>
@@ -634,8 +596,7 @@ const DomainsForm = () => {
values.domain
)
) {
errors.domain =
'Not a valid URL. Please use http:// or https://'
errors.domain = 'Not a valid URL. Please use http:// or https://'
}
return errors
}}
@@ -644,15 +605,14 @@ const DomainsForm = () => {
// console.log('errors in form', errors)
// console.log('touched in form', touched)
return (
<div className="space-y-4 pt-4 mb-4">
<div className="mb-4 space-y-4 pt-4">
<div className="px-5">
<p className="text-sm text-scale-1100">
This will add a domain to a list of allowed domains that
can interact with your Authenticaton services for this
project.
<p className="text-scale-1100 text-sm">
This will add a domain to a list of allowed domains that can interact with
your Authenticaton services for this project.
</p>
</div>
<div className="border-t border-overlay-border"></div>
<div className="border-overlay-border border-t"></div>
<div className="px-5">
<Input
id="domain"
@@ -661,7 +621,7 @@ const DomainsForm = () => {
placeholder="https://mydomain.com"
/>
</div>
<div className="border-t border-overlay-border"></div>
<div className="border-overlay-border border-t"></div>
<div className="px-5">
<Button
form="new-domain-form"
@@ -693,14 +653,14 @@ const DomainsForm = () => {
return (
<>
<div
className="bg-scale-300 px-6 py-4 border border-scale-500
first:rounded-tr first:rounded-tl
last:rounded-br last:rounded-bl
text-sm text-scale-1200
flex gap-2 items-center justify-between
className="bg-scale-300 border-scale-500 text-scale-1200 flex items-center
justify-between gap-2
border px-6
py-4 text-sm
first:rounded-tr first:rounded-tl last:rounded-br last:rounded-bl
"
>
<div className="flex gap-2 items-center font-mono">
<div className="flex items-center gap-2 font-mono">
<span className="text-scale-900">
<IconGlobe strokeWidth={2} size={14} />
</span>
@@ -726,7 +686,7 @@ const DomainsForm = () => {
visible={deleteOpen}
onCancel={() => setDeleteOpen(!open)}
header={
<div className="flex items-center gap-2 text-scale-1200">
<div className="text-scale-1200 flex items-center gap-2">
<div className="flex items-baseline gap-2">
<h3 className="text-sm">Remove domain</h3>
</div>
@@ -735,19 +695,18 @@ const DomainsForm = () => {
contentStyle={{ padding: 0 }}
hideFooter
>
<div className="space-y-4 pt-4 mb-4">
<div className="mb-4 space-y-4 pt-4">
<div className="px-5">
<p className="text-sm text-scale-1100 mb-2">
<p className="text-scale-1100 mb-2 text-sm">
Are you sure you want to remove{' '}
<span className="text-scale-1200">{selectedDomain}</span>?
</p>
<p className="text-sm text-scale-900">
This domain will no longer work with your Authentication
configuration.
<p className="text-scale-900 text-sm">
This domain will no longer work with your Authentication configuration.
</p>
</div>
<div className="border-t border-overlay-border"></div>
<div className="px-5 flex gap-3">
<div className="border-overlay-border border-t"></div>
<div className="flex gap-3 px-5">
<Button
type="secondary"
block
@@ -802,10 +761,10 @@ const SmtpForm = () => {
bg-scale-100
dark:bg-scale-300
border border-scale-400 rounded-md my-8 shadow overflow-hidden"
border-scale-400 my-8 overflow-hidden rounded-md border shadow"
>
{header && (
<div className="bg-scale-100 dark:bg-scale-200 px-8 py-4 border-b border-scale-400">
<div className="bg-scale-100 dark:bg-scale-200 border-scale-400 border-b px-8 py-4">
{header}
</div>
)}
@@ -821,7 +780,7 @@ const SmtpForm = () => {
children: React.ReactNode
header?: React.ReactNode
}) => (
<div className="grid grid-cols-12 px-8 py-2 gap-8">
<div className="grid grid-cols-12 gap-8 px-8 py-2">
{header && <div className="col-span-4">{header}</div>}
{children}
</div>
@@ -857,8 +816,8 @@ const SmtpForm = () => {
<Panel
footer={
<>
<div className="border-t border-scale-400"></div>
<div className="py-3 px-6 flex gap-2 justify-end">
<div className="border-scale-400 border-t"></div>
<div className="flex justify-end gap-2 py-3 px-6">
<Button
loading={isSubmitting}
type="secondary"
@@ -896,37 +855,22 @@ const SmtpForm = () => {
descriptionText="If this is disabled, new users will not be able to sign up to your application."
/>
{/* </Section> */}
<div className="border-t border-scale-400"></div>
<div className="border-scale-400 border-t"></div>
<Section
header={
<div className="w-full">
<label className="text-sm text-scale-1100">
SMTP details
</label>
<p className="text-sm text-scale-900">
<label className="text-scale-1100 text-sm">SMTP details</label>
<p className="text-scale-900 text-sm">
These settings can be found in your SMTP provider config
</p>
</div>
}
>
<div className="space-y-6 col-span-8">
<Input
label="Sender address"
type="url"
disabled={!values.enable_smtp}
/>
<Input
label="SMTP server host"
disabled={!values.enable_smtp}
/>
<InputNumber
label="SMTP server port"
disabled={!values.enable_smtp}
/>
<Input
label="SMTP account username"
disabled={!values.enable_smtp}
/>
<div className="col-span-8 space-y-6">
<Input label="Sender address" type="url" disabled={!values.enable_smtp} />
<Input label="SMTP server host" disabled={!values.enable_smtp} />
<InputNumber label="SMTP server port" disabled={!values.enable_smtp} />
<Input label="SMTP account username" disabled={!values.enable_smtp} />
<Input
label="SMTP account password"
type="password"
@@ -963,10 +907,10 @@ const TemplatesForm = () => {
className="
bg-scale-100
dark:bg-scale-300
border border-scale-400 rounded-md mb-8 shadow overflow-hidden"
border-scale-400 mb-8 overflow-hidden rounded-md border shadow"
>
{header && (
<div className="bg-scale-100 dark:bg-scale-200 px-8 py-4 border-b border-scale-400">
<div className="bg-scale-100 dark:bg-scale-200 border-scale-400 border-b px-8 py-4">
{header}
</div>
)}
@@ -982,7 +926,7 @@ const TemplatesForm = () => {
children: React.ReactNode
header?: React.ReactNode
}) => (
<div className="grid grid-cols-12 px-8 py-2 gap-8">
<div className="grid grid-cols-12 gap-8 px-8 py-2">
{header && <div className="col-span-4">{header}</div>}
{children}
</div>
@@ -1026,18 +970,15 @@ const TemplatesForm = () => {
<Tabs.Panel id={'confirm_signup'} label="Confirm Signup" />
<Tabs.Panel id={'reset_password'} label="Reset Password" />
<Tabs.Panel id={'magic_link'} label="Magic Link" />
<Tabs.Panel
id={'change_email_address'}
label="Change Email Address"
/>
<Tabs.Panel id={'change_email_address'} label="Change Email Address" />
<Tabs.Panel id={'invite_user'} label="Invite User" />
</Tabs>
</div>
<Panel
footer={
<>
<div className="border-t border-scale-400"></div>
<div className="py-3 px-6 flex gap-2 justify-end">
<div className="border-scale-400 border-t"></div>
<div className="flex justify-end gap-2 py-3 px-6">
<Button
loading={isSubmitting}
type="secondary"
@@ -1079,18 +1020,15 @@ const TemplatesForm = () => {
<Section
header={
<div className="w-full">
<h3 className="text-base text-scale-1200 mb-4">
Confirm Signup
</h3>
<p className="text-sm text-scale-900">
When a user signs up using an email address and password,
you can send them a confirmation email to verify their
registered email address. Learn more
<h3 className="text-scale-1200 mb-4 text-base">Confirm Signup</h3>
<p className="text-scale-900 text-sm">
When a user signs up using an email address and password, you can send them a
confirmation email to verify their registered email address. Learn more
</p>
</div>
}
>
<div className="space-y-6 col-span-8">
<div className="col-span-8 space-y-6">
<Input label="Sender address" type="url" />
<Input label="SMTP server host" />
<InputNumber label="SMTP server port" />
@@ -1125,34 +1063,28 @@ export function WholeForm() {
<div style={{ width: '820px' }} className="mx-auto">
<Tabs defaultActiveId="one" type="underlined" size="medium">
<Tabs.Panel id="one" label="Sign-in method">
<div className="space-y-12 py-12 animate-fade-in">
<div className="animate-fade-in space-y-12 py-12">
<div>
<h1 className="text-3xl text-scale-1200">Sign-in method</h1>
<h1 className="text-scale-1200 text-3xl">Sign-in method</h1>
</div>
<div className="border-t border-scale-400"></div>
<div className="border-scale-400 border-t"></div>
<div>
<h3 className="text-scale-1200 text-2xl mb-2">
General settings
</h3>
<p className="text-sm text-scale-900">
Turn payment methods on and off in one click – no engineering
time required.
<h3 className="text-scale-1200 mb-2 text-2xl">General settings</h3>
<p className="text-scale-900 text-sm">
Turn payment methods on and off in one click – no engineering time required.
</p>
<p className="text-sm text-scale-900">
Use our guide to check which payment methods are compatible with
your integration.
<p className="text-scale-900 text-sm">
Use our guide to check which payment methods are compatible with your integration.
</p>
<CardForm />
</div>
<div>
<h3 className="text-scale-1200 text-2xl mb-2">Providers</h3>
<p className="text-sm text-scale-900">
Turn payment methods on and off in one click – no engineering
time required.
<h3 className="text-scale-1200 mb-2 text-2xl">Providers</h3>
<p className="text-scale-900 text-sm">
Turn payment methods on and off in one click – no engineering time required.
</p>
<p className="text-sm text-scale-900">
Use our guide to check which payment methods are compatible with
your integration.
<p className="text-scale-900 text-sm">
Use our guide to check which payment methods are compatible with your integration.
</p>
<AuthForm />
</div>
@@ -1161,42 +1093,40 @@ export function WholeForm() {
</div>
</Tabs.Panel>
<Tabs.Panel id="two" label="Emails">
<div className="space-y-12 py-12 animate-fade-in">
<div className="animate-fade-in space-y-12 py-12">
<div>
<h1 className="text-3xl text-scale-1200">Emails</h1>
<h1 className="text-scale-1200 text-3xl">Emails</h1>
</div>
<button
className="
group
w-full
bg-scale-300
dark:bg-scale-100
border
border-scale-500 hover:border-scale-700
dark:border-scale-300 dark:hover:border-scale-500
px-6 py-4
my-6
flex gap-6 items-center
rounded-md shadow-sm
bg-scale-300
dark:bg-scale-100
border-scale-500
hover:border-scale-700
dark:border-scale-300
dark:hover:border-scale-500 group
my-6 flex
w-full items-center
gap-6
rounded-md border px-6
py-4 text-left
shadow-sm
transition
text-left
"
>
<div className="transition duration-500 bg-brand-900 rounded p-1 w-10 h-10 group-hover:-rotate-3 group-hover:scale-110">
<div className="bg-brand-900 h-10 w-10 rounded p-1 transition duration-500 group-hover:-rotate-3 group-hover:scale-110">
{/* <img
className="text-white w-12 h-12"
src="/icons/docs-illustration.svg"
/> */}
</div>
<div className="grow">
<h3 className="text-scale-1200">
Use authentication as the backbone of your app
</h3>
<h3 className="text-scale-1200">Use authentication as the backbone of your app</h3>
<p className="text-scale-900 text-xs">
Learn how to use Supabase Auth with Auth Policies to pick and
choose what users see what.
Learn how to use Supabase Auth with Auth Policies to pick and choose what users
see what.
</p>
<p className="text-brand-900 mt-2 text-xs flex items-center gap-1">
<p className="text-brand-900 mt-2 flex items-center gap-1 text-xs">
See Documenation
<span className="transition group-hover:translate-x-1">
<IconArrowRight strokeWidth={2} width={12} />
@@ -1207,20 +1137,18 @@ export function WholeForm() {
<IconX />
</div>
</button>
<div className="border-t border-scale-400"></div>
<div className="border-scale-400 border-t"></div>
<div>
<h3 className="text-scale-1200 text-2xl mb-2">Custom SMTP</h3>
<p className="text-sm text-scale-900">
You can use your own SMTP server instead of the built-in email
service.
<h3 className="text-scale-1200 mb-2 text-2xl">Custom SMTP</h3>
<p className="text-scale-900 text-sm">
You can use your own SMTP server instead of the built-in email service.
</p>
<SmtpForm />
</div>
<div>
<h3 className="text-scale-1200 text-2xl mb-2">Templates</h3>
<p className="text-sm text-scale-900">
You can use your own SMTP server instead of the built-in email
service.
<h3 className="text-scale-1200 mb-2 text-2xl">Templates</h3>
<p className="text-scale-900 text-sm">
You can use your own SMTP server instead of the built-in email service.
</p>
<TemplatesForm />
</div>
@@ -0,0 +1,12 @@
// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import * as React from 'react'
// @ts-ignore
import { Activity } from 'react-feather'
import IconBase from './../../IconBase'
function IconActivity(props: any) {
return <IconBase icon={Activity} {...props} />
}
export default IconActivity
@@ -1,6 +1,6 @@
// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import React from 'react'
import * as React from 'react'
// @ts-ignore
import { Airplay } from 'react-feather'
import IconBase from './../../IconBase'
@@ -1,6 +1,6 @@
// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import React from 'react'
import * as React from 'react'
// @ts-ignore
import { AlertCircle } from 'react-feather'
import IconBase from './../../IconBase'
@@ -1,6 +1,6 @@
// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import React from 'react'
import * as React from 'react'
// @ts-ignore
import { AlertOctagon } from 'react-feather'
import IconBase from './../../IconBase'
@@ -1,6 +1,6 @@
// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import React from 'react'
import * as React from 'react'
// @ts-ignore
import { AlertTriangle } from 'react-feather'
import IconBase from './../../IconBase'
@@ -1,6 +1,6 @@
// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import React from 'react'
import * as React from 'react'
// @ts-ignore
import { AlignCenter } from 'react-feather'
import IconBase from './../../IconBase'
@@ -1,6 +1,6 @@
// DO NOT EDIT
// this file is generated by /internals/populate-icons script
import React from 'react'
import * as React from 'react'
// @ts-ignore
import { AlignJustify } from 'react-feather'
import IconBase from './../../IconBase'
Loaded 100 of 1124 files, more files were not shown because too many files have changed in this diff. Show more