feat(ui): login & sign-up page for IDPE (#17049)
parent
a017f0c4a2
commit
4b3b1308f0
|
@ -6,6 +6,7 @@
|
|||
1. [17095](https://github.com/influxdata/influxdb/pull/17095): Extend pkger dashboards with table view support
|
||||
1. [17114](https://github.com/influxdata/influxdb/pull/17114): Allow for retention to be provided to influx setup command as a duration
|
||||
1. [17138](https://github.com/influxdata/influxdb/pull/17138): Extend pkger export all capabilities to support filtering by lable name and resource type
|
||||
1. [17049](https://github.com/influxdata/influxdb/pull/17049): Added new login and sign-up screen that for cloud users that allows direct login from their region
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
@ -19,6 +20,7 @@
|
|||
1. [17113](https://github.com/influxdata/influxdb/pull/17113): Disabled group functionality for check query builder
|
||||
1. [17120](https://github.com/influxdata/influxdb/pull/17120): Fixed cell configuration error that was popping up when users create a dashboard and accessed the disk usage cell for the first time
|
||||
1. [17097](https://github.com/influxdata/influxdb/pull/17097): Listing all the default variables in the VariableTab of the script editor
|
||||
1. [17049](https://github.com/influxdata/influxdb/pull/17049): Fixed bug that was preventing the interval status on the dashboard header from refreshing on selections
|
||||
|
||||
## v2.0.0-beta.5 [2020-02-27]
|
||||
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
"@influxdata/influxdb-templates": "0.9.0",
|
||||
"@influxdata/react-custom-scrollbars": "4.3.8",
|
||||
"abortcontroller-polyfill": "^1.3.0",
|
||||
"auth0-js": "^9.12.2",
|
||||
"axios": "^0.19.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"bignumber.js": "^4.0.2",
|
||||
|
@ -157,8 +158,8 @@
|
|||
"moment": "^2.13.0",
|
||||
"monaco-editor": "^0.19.2",
|
||||
"monaco-editor-textmate": "^2.2.1",
|
||||
"monaco-languageclient": "^0.11.0",
|
||||
"monaco-editor-webpack-plugin": "^1.8.2",
|
||||
"monaco-languageclient": "^0.11.0",
|
||||
"monaco-textmate": "^3.0.1",
|
||||
"normalizr": "^3.4.1",
|
||||
"onigasm": "^2.2.4",
|
||||
|
@ -181,6 +182,7 @@
|
|||
"react-router": "^3.0.2",
|
||||
"react-router-redux": "^4.0.8",
|
||||
"react-scrollbars-custom": "^4.0.0-alpha.8",
|
||||
"react-spring": "^8.0.27",
|
||||
"react-virtualized": "^9.18.5",
|
||||
"redux": "^4.0.0",
|
||||
"redux-auth-wrapper": "^1.0.0",
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
// Libraries
|
||||
import {PureComponent} from 'react'
|
||||
import {FC, useEffect} from 'react'
|
||||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
import auth0js from 'auth0-js'
|
||||
|
||||
// APIs
|
||||
import {postSignout} from 'src/client'
|
||||
import {getAuth0Config} from 'src/authorizations/apis'
|
||||
|
||||
// Constants
|
||||
import {CLOUD, CLOUD_URL, CLOUD_LOGOUT_PATH} from 'src/shared/constants'
|
||||
|
@ -11,19 +13,20 @@ import {CLOUD, CLOUD_URL, CLOUD_LOGOUT_PATH} from 'src/shared/constants'
|
|||
// Components
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
type Props = WithRouterProps
|
||||
// Utils
|
||||
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
|
||||
|
||||
@ErrorHandling
|
||||
export class Logout extends PureComponent<Props> {
|
||||
public componentDidMount() {
|
||||
this.handleSignOut()
|
||||
}
|
||||
|
||||
public render() {
|
||||
return null
|
||||
}
|
||||
|
||||
private handleSignOut = async () => {
|
||||
const Logout: FC<WithRouterProps> = ({router}) => {
|
||||
const handleSignOut = async () => {
|
||||
if (CLOUD && isFlagEnabled('regionBasedLoginPage')) {
|
||||
const config = await getAuth0Config()
|
||||
const auth0 = new auth0js.WebAuth({
|
||||
domain: config.domain,
|
||||
clientID: config.clientID,
|
||||
})
|
||||
auth0.logout({})
|
||||
return
|
||||
}
|
||||
if (CLOUD) {
|
||||
window.location.href = `${CLOUD_URL}${CLOUD_LOGOUT_PATH}`
|
||||
return
|
||||
|
@ -34,9 +37,14 @@ export class Logout extends PureComponent<Props> {
|
|||
throw new Error(resp.data.message)
|
||||
}
|
||||
|
||||
this.props.router.push(`/signin`)
|
||||
router.push(`/signin`)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
handleSignOut()
|
||||
}, [])
|
||||
return null
|
||||
}
|
||||
|
||||
export default withRouter<Props>(Logout)
|
||||
export default ErrorHandling(withRouter<WithRouterProps>(Logout))
|
||||
|
|
|
@ -19,6 +19,9 @@ import {CLOUD, CLOUD_SIGNIN_PATHNAME} from 'src/shared/constants'
|
|||
// Types
|
||||
import {RemoteDataState} from 'src/types'
|
||||
|
||||
// Utils
|
||||
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
|
||||
|
||||
interface State {
|
||||
loading: RemoteDataState
|
||||
}
|
||||
|
@ -81,6 +84,11 @@ export class Signin extends PureComponent<Props, State> {
|
|||
|
||||
clearInterval(this.intervalID)
|
||||
|
||||
if (CLOUD && isFlagEnabled('regionBasedLoginPage')) {
|
||||
this.props.router.replace('/login')
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: add returnTo to CLOUD signin
|
||||
if (CLOUD) {
|
||||
window.location.pathname = CLOUD_SIGNIN_PATHNAME
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import AJAX from 'src/utils/ajax'
|
||||
import {Authorization} from 'src/types'
|
||||
import {Authorization, Auth0Config} from 'src/types'
|
||||
|
||||
export const createAuthorization = async (
|
||||
authorization
|
||||
|
@ -17,3 +17,14 @@ export const createAuthorization = async (
|
|||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const getAuth0Config = async (): Promise<Auth0Config> => {
|
||||
try {
|
||||
const response = await fetch('/api/v2private/oauth/clientConfig')
|
||||
const data = await response.json()
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const GithubLogo: FC<Props> = ({className}) => (
|
||||
<svg
|
||||
className={classnames('github-logo', className)}
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<title>GitHub icon</title>
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
||||
</svg>
|
||||
)
|
|
@ -0,0 +1,33 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
interface Props {
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const GoogleLogo: FC<Props> = ({className}) => (
|
||||
<svg
|
||||
className={classnames('google-logo', className)}
|
||||
x="0"
|
||||
y="0"
|
||||
viewBox="0 0 17.6 18"
|
||||
>
|
||||
<path
|
||||
d="M15 15.8h-3v-2.3c1-.6 1.6-1.6 1.8-2.7H9V7.4h8.5c.1.6.2 1.2.2 1.8-.1 2.7-1 5.1-2.7 6.6z"
|
||||
className="google-logo--blue"
|
||||
/>
|
||||
<path
|
||||
d="M9 18c-3.5 0-6.6-2-8-5v-2.3h3c.7 2.1 2.7 3.7 5 3.7 1.2 0 2.2-.3 3-.9l2.9 2.3C13.5 17.2 11.4 18 9 18z"
|
||||
className="google-logo--green"
|
||||
/>
|
||||
<path
|
||||
d="M4 7.3c-.2.5-.3 1.1-.3 1.7 0 .6.1 1.2.3 1.7L1 13c-.6-1.2-1-2.6-1-4s.3-2.8 1-4h3v2.3z"
|
||||
className="google-logo--yellow"
|
||||
/>
|
||||
<path
|
||||
d="M12.4 4.9C11.5 4 10.3 3.6 9 3.6c-2.3 0-4.3 1.6-5 3.7L1 5c1.4-3 4.5-5 8-5 2.4 0 4.5.9 6 2.3l-2.6 2.6z"
|
||||
className="google-logo--red"
|
||||
/>
|
||||
</svg>
|
||||
)
|
|
@ -0,0 +1,19 @@
|
|||
import CSharpLogo from 'src/clientLibraries/graphics/CSharpLogo'
|
||||
import {GithubLogo} from 'src/clientLibraries/graphics/GithubLogo'
|
||||
import GoLogo from 'src/clientLibraries/graphics/GoLogo'
|
||||
import {GoogleLogo} from 'src/clientLibraries/graphics/GoogleLogo'
|
||||
import JavaLogo from 'src/clientLibraries/graphics/JavaLogo'
|
||||
import JSLogo from 'src/clientLibraries/graphics/JSLogo'
|
||||
import PythonLogo from 'src/clientLibraries/graphics/PythonLogo'
|
||||
import RubyLogo from 'src/clientLibraries/graphics/RubyLogo'
|
||||
|
||||
export {
|
||||
CSharpLogo,
|
||||
GithubLogo,
|
||||
GoLogo,
|
||||
GoogleLogo,
|
||||
JavaLogo,
|
||||
JSLogo,
|
||||
PythonLogo,
|
||||
RubyLogo,
|
||||
}
|
|
@ -20,6 +20,7 @@ import GetOrganizations from 'src/shared/containers/GetOrganizations'
|
|||
import Setup from 'src/Setup'
|
||||
import Signin from 'src/Signin'
|
||||
import SigninPage from 'src/onboarding/containers/SigninPage'
|
||||
import {LoginPage} from 'src/onboarding/containers/LoginPage'
|
||||
import Logout from 'src/Logout'
|
||||
import TaskPage from 'src/tasks/containers/TaskPage'
|
||||
import TasksPage from 'src/tasks/containers/TasksPage'
|
||||
|
@ -191,6 +192,7 @@ class Root extends PureComponent {
|
|||
component={OnboardingWizardPage}
|
||||
/>
|
||||
<Route component={UnauthenticatedApp}>
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/signin" component={SigninPage} />
|
||||
<Route path="/logout" component={Logout} />
|
||||
</Route>
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
// Libraries
|
||||
import React, {FC, useState, ChangeEvent} from 'react'
|
||||
import {
|
||||
Button,
|
||||
ButtonShape,
|
||||
ButtonType,
|
||||
Columns,
|
||||
ComponentColor,
|
||||
ComponentSize,
|
||||
ComponentStatus,
|
||||
Form,
|
||||
Grid,
|
||||
Input,
|
||||
InputType,
|
||||
VisibilityInput,
|
||||
} from '@influxdata/clockface'
|
||||
|
||||
// Types
|
||||
import {FormFieldValidation} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
buttonStatus: ComponentStatus
|
||||
emailValidation: FormFieldValidation
|
||||
email: string
|
||||
passwordValidation: FormFieldValidation
|
||||
password: string
|
||||
handleInputChange: (event: ChangeEvent<HTMLInputElement>) => void
|
||||
handleForgotPasswordClick: () => void
|
||||
}
|
||||
|
||||
export const LoginForm: FC<Props> = ({
|
||||
buttonStatus,
|
||||
emailValidation,
|
||||
email,
|
||||
passwordValidation,
|
||||
password,
|
||||
handleInputChange,
|
||||
handleForgotPasswordClick,
|
||||
}) => {
|
||||
const [isVisible, toggleVisibility] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Row className="sign-up--form-padded-row">
|
||||
<Grid.Column widthXS={Columns.Twelve}>
|
||||
<Form.Element
|
||||
label="Work Email Address"
|
||||
required={true}
|
||||
errorMessage={emailValidation.errorMessage}
|
||||
>
|
||||
<Input
|
||||
name="email"
|
||||
value={email}
|
||||
type={InputType.Email}
|
||||
size={ComponentSize.Large}
|
||||
status={
|
||||
emailValidation.hasError
|
||||
? ComponentStatus.Error
|
||||
: ComponentStatus.Default
|
||||
}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Grid.Column widthXS={Columns.Twelve}>
|
||||
<Form.Element
|
||||
label="Password"
|
||||
required={true}
|
||||
errorMessage={passwordValidation.errorMessage}
|
||||
>
|
||||
<VisibilityInput
|
||||
name="password"
|
||||
value={password}
|
||||
size={ComponentSize.Large}
|
||||
onChange={handleInputChange}
|
||||
visible={isVisible}
|
||||
status={
|
||||
passwordValidation.hasError
|
||||
? ComponentStatus.Error
|
||||
: ComponentStatus.Default
|
||||
}
|
||||
onToggleClick={() => toggleVisibility(!isVisible)}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
<a onClick={handleForgotPasswordClick} className="login--forgot-password">
|
||||
Forgot Password?
|
||||
</a>
|
||||
<Button
|
||||
className="create-account--button"
|
||||
text="Login"
|
||||
color={ComponentColor.Primary}
|
||||
size={ComponentSize.Large}
|
||||
type={ButtonType.Submit}
|
||||
status={buttonStatus}
|
||||
shape={ButtonShape.StretchToFit}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
// Libraries
|
||||
import React, {FC, useState, ChangeEvent} from 'react'
|
||||
import {
|
||||
Button,
|
||||
ButtonShape,
|
||||
ButtonType,
|
||||
Columns,
|
||||
ComponentColor,
|
||||
ComponentSize,
|
||||
ComponentStatus,
|
||||
Form,
|
||||
Grid,
|
||||
Input,
|
||||
InputType,
|
||||
VisibilityInput,
|
||||
} from '@influxdata/clockface'
|
||||
|
||||
// Types
|
||||
import {FormFieldValidation} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
buttonStatus: ComponentStatus
|
||||
confirmPassword: string
|
||||
confirmPasswordValidation: FormFieldValidation
|
||||
email: string
|
||||
emailValidation: FormFieldValidation
|
||||
firstName: string
|
||||
firstNameValidation: FormFieldValidation
|
||||
lastName: string
|
||||
lastNameValidation: FormFieldValidation
|
||||
password: string
|
||||
passwordValidation: FormFieldValidation
|
||||
handleInputChange: (event: ChangeEvent<HTMLInputElement>) => void
|
||||
}
|
||||
|
||||
export const SignUpForm: FC<Props> = ({
|
||||
buttonStatus,
|
||||
confirmPassword,
|
||||
confirmPasswordValidation,
|
||||
email,
|
||||
emailValidation,
|
||||
firstName,
|
||||
firstNameValidation,
|
||||
lastName,
|
||||
lastNameValidation,
|
||||
password,
|
||||
passwordValidation,
|
||||
handleInputChange,
|
||||
}) => {
|
||||
const [isVisible, toggleVisibility] = useState(false)
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Grid.Row className="sign-up--form-padded-row">
|
||||
<Grid.Column widthXS={Columns.Six}>
|
||||
<Form.Element
|
||||
label="First Name"
|
||||
required={true}
|
||||
errorMessage={firstNameValidation.errorMessage}
|
||||
>
|
||||
<Input
|
||||
name="firstName"
|
||||
value={firstName}
|
||||
autoFocus={true}
|
||||
size={ComponentSize.Large}
|
||||
status={
|
||||
firstNameValidation.hasError
|
||||
? ComponentStatus.Error
|
||||
: ComponentStatus.Default
|
||||
}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Grid.Column>
|
||||
<Grid.Column widthXS={Columns.Six}>
|
||||
<Form.Element
|
||||
label="Last Name"
|
||||
required={true}
|
||||
errorMessage={lastNameValidation.errorMessage}
|
||||
>
|
||||
<Input
|
||||
name="lastName"
|
||||
value={lastName}
|
||||
size={ComponentSize.Large}
|
||||
status={
|
||||
lastNameValidation.hasError
|
||||
? ComponentStatus.Error
|
||||
: ComponentStatus.Default
|
||||
}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Grid.Column widthXS={Columns.Twelve}>
|
||||
<Form.Element
|
||||
label="Work Email Address"
|
||||
required={true}
|
||||
errorMessage={emailValidation.errorMessage}
|
||||
>
|
||||
<Input
|
||||
name="email"
|
||||
value={email}
|
||||
type={InputType.Email}
|
||||
size={ComponentSize.Large}
|
||||
status={
|
||||
emailValidation.hasError
|
||||
? ComponentStatus.Error
|
||||
: ComponentStatus.Default
|
||||
}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
<Grid.Row>
|
||||
<Grid.Column widthXS={Columns.Twelve}>
|
||||
<Form.Element
|
||||
label="Password"
|
||||
required={true}
|
||||
errorMessage={passwordValidation.errorMessage}
|
||||
>
|
||||
<VisibilityInput
|
||||
name="password"
|
||||
value={password}
|
||||
size={ComponentSize.Large}
|
||||
onChange={handleInputChange}
|
||||
visible={isVisible}
|
||||
status={
|
||||
passwordValidation.hasError
|
||||
? ComponentStatus.Error
|
||||
: ComponentStatus.Default
|
||||
}
|
||||
onToggleClick={() => toggleVisibility(!isVisible)}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Grid.Column>
|
||||
<Grid.Column widthXS={Columns.Twelve}>
|
||||
<Form.Element
|
||||
label="Confirm Password"
|
||||
required={true}
|
||||
errorMessage={confirmPasswordValidation.errorMessage}
|
||||
>
|
||||
<VisibilityInput
|
||||
name="confirmPassword"
|
||||
value={confirmPassword}
|
||||
size={ComponentSize.Large}
|
||||
onChange={handleInputChange}
|
||||
visible={isVisible}
|
||||
status={
|
||||
confirmPasswordValidation.hasError
|
||||
? ComponentStatus.Error
|
||||
: ComponentStatus.Default
|
||||
}
|
||||
onToggleClick={() => toggleVisibility(!isVisible)}
|
||||
/>
|
||||
</Form.Element>
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
<Button
|
||||
className="create-account--button"
|
||||
text="Create Account"
|
||||
color={ComponentColor.Primary}
|
||||
size={ComponentSize.Large}
|
||||
type={ButtonType.Submit}
|
||||
status={buttonStatus}
|
||||
shape={ButtonShape.StretchToFit}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
.clockface--app-wrapper.sign-up--page {
|
||||
background-image: linear-gradient(
|
||||
47deg,
|
||||
rgba(19, 0, 45, 0) 65%,
|
||||
rgba(191, 47, 229, 0.4) 100%
|
||||
);
|
||||
background-color: #13002d;
|
||||
}
|
||||
|
||||
.create-account--button {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.github-logo {
|
||||
width: $icon-size-md;
|
||||
height: $icon-size-md;
|
||||
fill: $g20-white;
|
||||
}
|
||||
|
||||
.google-logo {
|
||||
height: $icon-size-md;
|
||||
width: $icon-size-md;
|
||||
|
||||
.google-logo--blue {
|
||||
fill: #3e82f1;
|
||||
}
|
||||
|
||||
.google-logo--green {
|
||||
fill: #32a753;
|
||||
}
|
||||
|
||||
.google-logo--red {
|
||||
fill: #e74133;
|
||||
}
|
||||
|
||||
.google-logo--yellow {
|
||||
fill: #f9bb00;
|
||||
}
|
||||
}
|
||||
|
||||
.login--forgot-password {
|
||||
color: $c-pool;
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
line-height: 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sign-up--form {
|
||||
min-width: 330px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.sign-up--form-padded-row {
|
||||
padding-top: $ix-marg-d;
|
||||
}
|
||||
}
|
||||
|
||||
.sign-up--form-panel {
|
||||
max-width: 500px;
|
||||
width: 400px;
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.sign-up--login-container {
|
||||
padding: $ix-marg-b;
|
||||
text-align: center;
|
||||
color: $g20-white;
|
||||
}
|
||||
|
||||
.sign-up--login-text {
|
||||
font-size: 16px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.sign-up--page-contents {
|
||||
.cf-page-contents--padding {
|
||||
height: 100vh;
|
||||
display: inline-flex;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.sign-up--panel {
|
||||
height: 100%;
|
||||
max-width: 1600px;
|
||||
min-height: 670px + $ix-marg-d;
|
||||
padding: $ix-marg-d;
|
||||
margin: auto;
|
||||
z-index: 0;
|
||||
background: transparent !important;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@media screen and (min-width: $grid--breakpoint-md) {
|
||||
.sign-up--form-panel {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.sign-up--full-height {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.sign-up--header-align {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sign-up--image {
|
||||
display: inline-block;
|
||||
position: fixed;
|
||||
z-index: 0;
|
||||
top: 20%;
|
||||
right: 7%;
|
||||
width: 160%;
|
||||
height: 160%;
|
||||
}
|
||||
|
||||
.sign-up--panel:not(.aws) {
|
||||
height: 80vh;
|
||||
padding: 64px;
|
||||
}
|
||||
|
||||
.sign-up--panel {
|
||||
max-height: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
.sign-up--social-button-group {
|
||||
position: relative;
|
||||
top: $ix-marg-b;
|
||||
text-align: center;
|
||||
|
||||
.signup-text {
|
||||
position: relative;
|
||||
right: $ix-marg-c;
|
||||
}
|
||||
|
||||
.signup-icon {
|
||||
width: $form-sm-height;
|
||||
height: $form-sm-height;
|
||||
background: transparent;
|
||||
position: relative;
|
||||
float: left;
|
||||
top: $icon-margin;
|
||||
}
|
||||
}
|
||||
|
||||
.sign-up--social-header {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.sign-up--or {
|
||||
position: relative;
|
||||
top: 7px;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {
|
||||
AlignItems,
|
||||
AppWrapper,
|
||||
Columns,
|
||||
FlexBox,
|
||||
FlexDirection,
|
||||
Grid,
|
||||
JustifyContent,
|
||||
Page,
|
||||
Panel,
|
||||
} from '@influxdata/clockface'
|
||||
|
||||
// Components
|
||||
import ErrorBoundary from 'src/shared/components/ErrorBoundary'
|
||||
import LoginPageContents from 'src/onboarding/containers/LoginPageContents'
|
||||
|
||||
export const LoginPage: FC = () => (
|
||||
<ErrorBoundary>
|
||||
<AppWrapper className="sign-up--page">
|
||||
<Page titleTag="Sign Up for InfluxDB Cloud">
|
||||
<Page.Contents
|
||||
scrollable={true}
|
||||
fullWidth={true}
|
||||
className="sign-up--page-contents"
|
||||
>
|
||||
<Panel className="sign-up--panel">
|
||||
<FlexBox
|
||||
direction={FlexDirection.Column}
|
||||
stretchToFitHeight={true}
|
||||
justifyContent={JustifyContent.Center}
|
||||
alignItems={AlignItems.Center}
|
||||
>
|
||||
<Grid.Row className="sign-up--full-height">
|
||||
<Grid.Column
|
||||
widthXS={Columns.Twelve}
|
||||
widthMD={Columns.Five}
|
||||
offsetMD={Columns.Four}
|
||||
widthLG={Columns.Four}
|
||||
className="sign-up--full-height"
|
||||
>
|
||||
<LoginPageContents />
|
||||
</Grid.Column>
|
||||
</Grid.Row>
|
||||
</FlexBox>
|
||||
</Panel>
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
</AppWrapper>
|
||||
</ErrorBoundary>
|
||||
)
|
|
@ -0,0 +1,459 @@
|
|||
// Libraries
|
||||
import React, {PureComponent, ChangeEvent, FormEvent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {
|
||||
AlignItems,
|
||||
ComponentColor,
|
||||
ComponentSize,
|
||||
ComponentStatus,
|
||||
FlexBox,
|
||||
FlexDirection,
|
||||
Grid,
|
||||
JustifyContent,
|
||||
Method,
|
||||
Panel,
|
||||
SelectGroup,
|
||||
} from '@influxdata/clockface'
|
||||
import auth0js, {WebAuth} from 'auth0-js'
|
||||
|
||||
// Components
|
||||
import {LoginForm} from 'src/onboarding/components/LoginForm'
|
||||
import {SignUpForm} from 'src/onboarding/components/SignUpForm'
|
||||
import {SocialButton} from 'src/shared/components/SocialButton'
|
||||
import {GoogleLogo, GithubLogo} from 'src/clientLibraries/graphics'
|
||||
import {Transition, animated} from 'react-spring/renderprops'
|
||||
|
||||
// Types
|
||||
import {Auth0Connection, FormFieldValidation} from 'src/types'
|
||||
|
||||
// APIs & Actions
|
||||
import {notify} from 'src/shared/actions/notifications'
|
||||
import {passwordResetSuccessfully} from 'src/shared/copy/notifications'
|
||||
import {getAuth0Config} from 'src/authorizations/apis'
|
||||
|
||||
interface ErrorObject {
|
||||
[key: string]: string | undefined
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
onNotify: typeof notify
|
||||
}
|
||||
|
||||
enum ActiveTab {
|
||||
SignUp = 'signup',
|
||||
Login = 'login',
|
||||
}
|
||||
|
||||
interface State {
|
||||
activeTab: ActiveTab
|
||||
buttonStatus: ComponentStatus
|
||||
confirmPassword: string
|
||||
confirmPasswordError: string
|
||||
email: string
|
||||
emailError: string
|
||||
firstName: string
|
||||
firstNameError: string
|
||||
lastName: string
|
||||
lastNameError: string
|
||||
password: string
|
||||
passwordError: string
|
||||
}
|
||||
|
||||
class LoginPageContents extends PureComponent<DispatchProps> {
|
||||
private auth0?: typeof WebAuth
|
||||
|
||||
state: State = {
|
||||
activeTab: ActiveTab.Login,
|
||||
buttonStatus: ComponentStatus.Default,
|
||||
confirmPassword: '',
|
||||
confirmPasswordError: '',
|
||||
email: '',
|
||||
emailError: '',
|
||||
firstName: '',
|
||||
firstNameError: '',
|
||||
lastName: '',
|
||||
lastNameError: '',
|
||||
password: '',
|
||||
passwordError: '',
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
try {
|
||||
const config = await getAuth0Config()
|
||||
this.auth0 = auth0js.WebAuth({
|
||||
domain: config.domain,
|
||||
clientID: config.clientID,
|
||||
redirectUri: config.redirectURL,
|
||||
responseType: 'code',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
activeTab,
|
||||
buttonStatus,
|
||||
confirmPassword,
|
||||
confirmPasswordError,
|
||||
email,
|
||||
emailError,
|
||||
firstName,
|
||||
firstNameError,
|
||||
lastName,
|
||||
lastNameError,
|
||||
password,
|
||||
passwordError,
|
||||
} = this.state
|
||||
|
||||
const loginTabActive = activeTab === ActiveTab.Login
|
||||
|
||||
return (
|
||||
<form
|
||||
action="/signup"
|
||||
method={Method.Post}
|
||||
onSubmit={this.handleSubmit}
|
||||
className="sign-up--form"
|
||||
>
|
||||
<div className="sign-up--login-container">
|
||||
<h2>Create your Free InfluxDB Cloud Account</h2>
|
||||
<p className="sign-up--login-text">No credit card required</p>
|
||||
</div>
|
||||
<Panel className="sign-up--form-panel">
|
||||
<Panel.Header size={ComponentSize.Large}>
|
||||
<Grid>
|
||||
<Grid.Row>
|
||||
<p className="sign-up--social-header">Continue with</p>
|
||||
</Grid.Row>
|
||||
<Grid.Row className="sign-up--social-button-group">
|
||||
<FlexBox
|
||||
stretchToFitWidth={true}
|
||||
direction={FlexDirection.Column}
|
||||
justifyContent={JustifyContent.Center}
|
||||
alignItems={AlignItems.Center}
|
||||
margin={ComponentSize.Large}
|
||||
>
|
||||
<SocialButton
|
||||
handleClick={() => {
|
||||
this.handleSocialClick(Auth0Connection.Google)
|
||||
}}
|
||||
buttonText="Google"
|
||||
>
|
||||
<GoogleLogo className="signup-icon" />
|
||||
</SocialButton>
|
||||
<SocialButton
|
||||
buttonText="Github"
|
||||
handleClick={() => {
|
||||
this.handleSocialClick(Auth0Connection.Github)
|
||||
}}
|
||||
>
|
||||
<GithubLogo className="signup-icon" />
|
||||
</SocialButton>
|
||||
</FlexBox>
|
||||
</Grid.Row>
|
||||
</Grid>
|
||||
</Panel.Header>
|
||||
<div className="sign-up--or">
|
||||
<p className="sign-up--social-header">OR</p>
|
||||
</div>
|
||||
<Panel.Body size={ComponentSize.Large}>
|
||||
<div>
|
||||
<FlexBox
|
||||
stretchToFitWidth={true}
|
||||
direction={FlexDirection.Row}
|
||||
justifyContent={JustifyContent.Center}
|
||||
>
|
||||
<SelectGroup
|
||||
size={ComponentSize.Large}
|
||||
color={ComponentColor.Default}
|
||||
>
|
||||
<SelectGroup.Option
|
||||
titleText="Login"
|
||||
value={ActiveTab.Login}
|
||||
id="login-option"
|
||||
active={loginTabActive}
|
||||
onClick={this.handleTabChange}
|
||||
>
|
||||
Login
|
||||
</SelectGroup.Option>
|
||||
<SelectGroup.Option
|
||||
titleText="Sign Up"
|
||||
value={ActiveTab.SignUp}
|
||||
id="signup-option"
|
||||
active={!loginTabActive}
|
||||
onClick={this.handleTabChange}
|
||||
>
|
||||
Sign Up
|
||||
</SelectGroup.Option>
|
||||
</SelectGroup>
|
||||
</FlexBox>
|
||||
</div>
|
||||
<Transition
|
||||
native
|
||||
reset
|
||||
unique
|
||||
items={loginTabActive}
|
||||
from={{height: 0}}
|
||||
enter={[
|
||||
{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
height: 'auto',
|
||||
},
|
||||
]}
|
||||
leave={{height: 0}}
|
||||
>
|
||||
{shouldShow =>
|
||||
shouldShow &&
|
||||
(props => (
|
||||
<animated.div style={props}>
|
||||
<LoginForm
|
||||
buttonStatus={buttonStatus}
|
||||
email={email}
|
||||
emailValidation={this.formFieldTypeFactory(emailError)}
|
||||
password={password}
|
||||
passwordValidation={this.formFieldTypeFactory(
|
||||
passwordError
|
||||
)}
|
||||
handleInputChange={this.handleInputChange}
|
||||
handleForgotPasswordClick={this.handleForgotPasswordClick}
|
||||
/>
|
||||
</animated.div>
|
||||
))
|
||||
}
|
||||
</Transition>
|
||||
<Transition
|
||||
native
|
||||
reset
|
||||
unique
|
||||
items={loginTabActive === false}
|
||||
from={{height: 0}}
|
||||
enter={[
|
||||
{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
height: 'auto',
|
||||
},
|
||||
]}
|
||||
leave={{height: 0}}
|
||||
>
|
||||
{shouldShow =>
|
||||
shouldShow &&
|
||||
(props => (
|
||||
<animated.div style={props}>
|
||||
<SignUpForm
|
||||
buttonStatus={buttonStatus}
|
||||
confirmPassword={confirmPassword}
|
||||
confirmPasswordValidation={this.formFieldTypeFactory(
|
||||
confirmPasswordError
|
||||
)}
|
||||
email={email}
|
||||
emailValidation={this.formFieldTypeFactory(emailError)}
|
||||
firstName={firstName}
|
||||
firstNameValidation={this.formFieldTypeFactory(
|
||||
firstNameError
|
||||
)}
|
||||
lastName={lastName}
|
||||
lastNameValidation={this.formFieldTypeFactory(
|
||||
lastNameError
|
||||
)}
|
||||
password={password}
|
||||
passwordValidation={this.formFieldTypeFactory(
|
||||
passwordError
|
||||
)}
|
||||
handleInputChange={this.handleInputChange}
|
||||
/>
|
||||
</animated.div>
|
||||
))
|
||||
}
|
||||
</Transition>
|
||||
</Panel.Body>
|
||||
</Panel>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
private get validateFieldValues(): {
|
||||
isValid: boolean
|
||||
errors: {[fieldName: string]: string}
|
||||
} {
|
||||
const {
|
||||
activeTab,
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
password,
|
||||
confirmPassword,
|
||||
} = this.state
|
||||
|
||||
const passwordsMatch = confirmPassword === password
|
||||
|
||||
const firstNameError = firstName === '' ? 'First name is required' : ''
|
||||
const lastNameError = lastName === '' ? 'Last name is required' : ''
|
||||
const emailError = email === '' ? 'Email is required' : ''
|
||||
const passwordError = password === '' ? 'Password is required' : ''
|
||||
|
||||
let confirmPasswordError = passwordsMatch
|
||||
? ''
|
||||
: "The input passwords don't match"
|
||||
if (confirmPassword === '') {
|
||||
confirmPasswordError = 'Confirm password is required'
|
||||
}
|
||||
|
||||
const errors: ErrorObject = {
|
||||
emailError,
|
||||
passwordError,
|
||||
}
|
||||
if (activeTab === ActiveTab.SignUp) {
|
||||
errors.firstNameError = firstNameError
|
||||
errors.lastNameError = lastNameError
|
||||
errors.confirmPasswordError = confirmPasswordError
|
||||
}
|
||||
|
||||
const isValid = Object.values(errors).every(error => error === '')
|
||||
|
||||
return {isValid, errors}
|
||||
}
|
||||
|
||||
private formFieldTypeFactory = (
|
||||
errorMessage: string
|
||||
): FormFieldValidation => ({
|
||||
errorMessage,
|
||||
hasError: errorMessage !== '',
|
||||
})
|
||||
|
||||
private handleSubmit = (event: FormEvent) => {
|
||||
const {isValid, errors} = this.validateFieldValues
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
firstName: given_name,
|
||||
lastName: family_name,
|
||||
activeTab,
|
||||
} = this.state
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
if (!isValid) {
|
||||
this.setState(errors)
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({buttonStatus: ComponentStatus.Loading})
|
||||
|
||||
if (activeTab === ActiveTab.Login) {
|
||||
this.auth0.login(
|
||||
{
|
||||
realm: Auth0Connection.Authentication,
|
||||
email,
|
||||
password,
|
||||
},
|
||||
error => {
|
||||
if (error) {
|
||||
this.setState({buttonStatus: ComponentStatus.Default})
|
||||
return this.displayErrorMessage(errors, error)
|
||||
}
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
this.auth0.signup(
|
||||
{
|
||||
connection: Auth0Connection.Authentication,
|
||||
email,
|
||||
password,
|
||||
family_name,
|
||||
given_name,
|
||||
},
|
||||
error => {
|
||||
if (error) {
|
||||
this.displayErrorMessage(errors, error)
|
||||
this.setState({buttonStatus: ComponentStatus.Default})
|
||||
return
|
||||
}
|
||||
// log the user into Auth0
|
||||
this.auth0.login(
|
||||
{
|
||||
realm: Auth0Connection.Authentication,
|
||||
email,
|
||||
password,
|
||||
},
|
||||
error => {
|
||||
if (error) {
|
||||
this.setState({buttonStatus: ComponentStatus.Default})
|
||||
this.displayErrorMessage(errors, error)
|
||||
return
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private displayErrorMessage = (errors, auth0Err) => {
|
||||
// eslint-disable-next-line
|
||||
if (/error in email/.test(auth0Err.code)) {
|
||||
this.setState({
|
||||
...errors,
|
||||
emailError: 'Please enter a valid email address',
|
||||
})
|
||||
} else if (auth0Err.code === 'user_exists') {
|
||||
const emailError = `An account with that email address already exists. Try logging in instead.`
|
||||
this.setState({...errors, emailError})
|
||||
} else {
|
||||
const emailError = `We have been notified of an issue while creating your account. If this issue persists, please contact support@influxdata.com`
|
||||
this.setState({...errors, emailError})
|
||||
}
|
||||
}
|
||||
|
||||
private handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({[event.target.name]: event.target.value})
|
||||
}
|
||||
|
||||
private handleTabChange = (value: ActiveTab) => {
|
||||
this.setState({activeTab: value})
|
||||
}
|
||||
|
||||
private handleSocialClick = (connection: Auth0Connection) => {
|
||||
this.auth0.authorize({
|
||||
connection,
|
||||
})
|
||||
}
|
||||
|
||||
private handleForgotPasswordClick = () => {
|
||||
const {email} = this.state
|
||||
const {onNotify} = this.props
|
||||
if (!email) {
|
||||
this.setState({emailError: 'Please enter a valid email address'})
|
||||
return
|
||||
}
|
||||
this.auth0.changePassword(
|
||||
{
|
||||
email,
|
||||
connection: Auth0Connection.Authentication,
|
||||
},
|
||||
(error, successMessage) => {
|
||||
if (error) {
|
||||
this.setState({emailError: error.message})
|
||||
return
|
||||
}
|
||||
// notify user that change password email was sent successfully
|
||||
// By default auth0 will send a success message even if the operation fails:
|
||||
// https://community.auth0.com/t/auth0-changepassword-always-returns-ok-even-when-user-is-not-found/11081/8
|
||||
onNotify(passwordResetSuccessfully(successMessage))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mdtp: DispatchProps = {
|
||||
onNotify: notify,
|
||||
}
|
||||
|
||||
export default connect<{}, DispatchProps>(
|
||||
null,
|
||||
mdtp
|
||||
)(LoginPageContents)
|
|
@ -0,0 +1,26 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {ButtonBase, ButtonShape, ComponentSize} from '@influxdata/clockface'
|
||||
|
||||
interface Props {
|
||||
buttonText: string
|
||||
children: JSX.Element
|
||||
handleClick?: () => void
|
||||
}
|
||||
|
||||
export const SocialButton: FC<Props> = ({
|
||||
buttonText,
|
||||
children,
|
||||
handleClick,
|
||||
}) => {
|
||||
return (
|
||||
<ButtonBase
|
||||
onClick={handleClick}
|
||||
size={ComponentSize.Large}
|
||||
shape={ButtonShape.StretchToFit}
|
||||
>
|
||||
{children}
|
||||
<span className="signup-text">{buttonText}</span>
|
||||
</ButtonBase>
|
||||
)
|
||||
}
|
|
@ -722,6 +722,12 @@ export const authorizationCreateSuccess = (): Notification => ({
|
|||
message: 'Token was created successfully',
|
||||
})
|
||||
|
||||
export const passwordResetSuccessfully = (message: string): Notification => ({
|
||||
...defaultSuccessNotification,
|
||||
message: `${message}
|
||||
If you haven't received an email, please ensure that the email you provided is correct.`,
|
||||
})
|
||||
|
||||
export const authorizationCreateFailed = (): Notification => ({
|
||||
...defaultErrorNotification,
|
||||
message: 'Failed to create tokens',
|
||||
|
|
|
@ -7,6 +7,7 @@ export const OSS_FLAGS = {
|
|||
telegrafEditor: false,
|
||||
customCheckQuery: false,
|
||||
matchingNotificationRules: false,
|
||||
regionBasedLoginPage: false,
|
||||
}
|
||||
|
||||
export const CLOUD_FLAGS = {
|
||||
|
@ -17,6 +18,7 @@ export const CLOUD_FLAGS = {
|
|||
telegrafEditor: false,
|
||||
customCheckQuery: false,
|
||||
matchingNotificationRules: false,
|
||||
regionBasedLoginPage: false,
|
||||
}
|
||||
|
||||
export const isFlagEnabled = (flagName: string, equals?: string | boolean) => {
|
||||
|
|
|
@ -60,7 +60,7 @@ $ix-link-warning-hover: $c-thunder;
|
|||
$ix-link-info: $c-star;
|
||||
$ix-link-info-hover: $c-comet;
|
||||
$ix-link-danger: $c-curacao;
|
||||
$ix-link-danger-hover: $c-dreamsicle;
|
||||
$ix-link-danger-hover: $c-dreamsicle;
|
||||
|
||||
$ix-text-default: $g13-mist;
|
||||
$ix-text-light: $g13-mist;
|
||||
|
@ -77,9 +77,21 @@ $ix-text-tiny: 11px;
|
|||
$ix-text-base: 12px;
|
||||
$ix-text-base-1: (ceil($ix-text-base * $ix-text-scale));
|
||||
$ix-text-base-2: (ceil($ix-text-base * $ix-text-scale * $ix-text-scale));
|
||||
$ix-text-base-3: (ceil($ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale));
|
||||
$ix-text-base-4: (ceil($ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale * $ix-text-scale));
|
||||
$ix-text-base-5: (ceil($ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale * $ix-text-scale * $ix-text-scale));
|
||||
$ix-text-base-3: (
|
||||
ceil($ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale)
|
||||
);
|
||||
$ix-text-base-4: (
|
||||
ceil(
|
||||
$ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale *
|
||||
$ix-text-scale
|
||||
)
|
||||
);
|
||||
$ix-text-base-5: (
|
||||
ceil(
|
||||
$ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale *
|
||||
$ix-text-scale * $ix-text-scale
|
||||
)
|
||||
);
|
||||
|
||||
/* Form Element Sizing */
|
||||
|
||||
|
@ -107,4 +119,8 @@ $default-font: 'Roboto', Helvetica, sans-serif;
|
|||
$code-font: 'RobotoMono', monospace;
|
||||
|
||||
/* Event Markers */
|
||||
$event-marker-height: 20px;
|
||||
$event-marker-height: 20px;
|
||||
|
||||
/* Icon Formatting */
|
||||
$icon-margin: 6px;
|
||||
$icon-size-md: 70px;
|
||||
|
|
|
@ -109,6 +109,7 @@
|
|||
@import 'src/shared/components/dapperScrollbars/DapperScrollbars.scss';
|
||||
@import 'src/templates/components/createFromTemplateOverlay/CreateFromTemplateOverlay.scss';
|
||||
@import 'src/onboarding/components/SigninForm.scss';
|
||||
@import 'src/onboarding/containers/LoginPage.scss';
|
||||
@import 'src/shared/components/ThresholdsSettings.scss';
|
||||
@import 'src/shared/components/ThresholdMarkers.scss';
|
||||
@import 'src/shared/components/EventMarkers.scss';
|
||||
|
|
|
@ -1 +1,13 @@
|
|||
export {Authorization, Permission} from 'src/client'
|
||||
|
||||
export enum Auth0Connection {
|
||||
Google = 'google-oauth2',
|
||||
Github = 'github',
|
||||
Authentication = 'Username-Password-Authentication',
|
||||
}
|
||||
|
||||
export type Auth0Config = {
|
||||
clientID: string
|
||||
domain: string
|
||||
redirectURL: string
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export interface FormFieldValidation {
|
||||
hasError: boolean
|
||||
errorMessage: string
|
||||
}
|
|
@ -12,6 +12,7 @@ export * from './dataExplorer'
|
|||
export * from './dataLoaders'
|
||||
export * from './filterEditor'
|
||||
export * from './flux'
|
||||
export * from './form'
|
||||
export * from './histogram'
|
||||
export * from './hosts'
|
||||
export * from './influxAdmin'
|
||||
|
|
139
ui/yarn.lock
139
ui/yarn.lock
|
@ -915,6 +915,13 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.12.0"
|
||||
|
||||
"@babel/runtime@^7.3.1":
|
||||
version "7.8.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
|
||||
integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/template@^7.1.0", "@babel/template@^7.2.2", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
|
||||
version "7.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
|
||||
|
@ -2184,6 +2191,19 @@ atob@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||
|
||||
auth0-js@^9.12.2:
|
||||
version "9.12.2"
|
||||
resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-9.12.2.tgz#8227259a94e8a47eecf8d7a630d99669049833a6"
|
||||
integrity sha512-0VfPu5UcgkGKQc7Q8KPqgkqqhLgXGsDCro2tde7hHPYK9JEzOyq82v0szUTHWlwQE1VT8K2/qZAsGDf7hFjI7g==
|
||||
dependencies:
|
||||
base64-js "^1.3.0"
|
||||
idtoken-verifier "^2.0.1"
|
||||
js-cookie "^2.2.0"
|
||||
qs "^6.7.0"
|
||||
superagent "^3.8.3"
|
||||
url-join "^4.0.1"
|
||||
winchan "^0.2.2"
|
||||
|
||||
autoprefixer@^6.3.1:
|
||||
version "6.7.7"
|
||||
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
|
||||
|
@ -2320,6 +2340,11 @@ base64-js@^1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
|
||||
integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==
|
||||
|
||||
base64-js@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
||||
|
||||
base@^0.11.1:
|
||||
version "0.11.2"
|
||||
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
||||
|
@ -3256,7 +3281,7 @@ commondir@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
|
||||
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
|
||||
|
||||
component-emitter@^1.2.1:
|
||||
component-emitter@^1.2.0, component-emitter@^1.2.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
|
||||
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
|
||||
|
@ -3370,6 +3395,11 @@ cookie@0.4.0:
|
|||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
||||
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
||||
|
||||
cookiejar@^2.1.0:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c"
|
||||
integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==
|
||||
|
||||
copy-concurrently@^1.0.0:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
|
||||
|
@ -3543,6 +3573,11 @@ crypto-browserify@^3.11.0:
|
|||
randombytes "^2.0.0"
|
||||
randomfill "^1.0.3"
|
||||
|
||||
crypto-js@^3.2.1:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
|
||||
integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
|
||||
|
||||
css-color-names@0.0.4, css-color-names@^0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
|
||||
|
@ -4438,6 +4473,11 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0:
|
|||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
es6-promise@^4.2.8:
|
||||
version "4.2.8"
|
||||
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
|
||||
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
|
||||
|
||||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
|
@ -5172,6 +5212,15 @@ fork-ts-checker-webpack-plugin@^1.4.3:
|
|||
tapable "^1.0.0"
|
||||
worker-rpc "^0.1.0"
|
||||
|
||||
form-data@^2.3.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
|
||||
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@~2.3.2:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
|
||||
|
@ -5181,6 +5230,11 @@ form-data@~2.3.2:
|
|||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formidable@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659"
|
||||
integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==
|
||||
|
||||
forwarded@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||
|
@ -5941,6 +5995,18 @@ identity-obj-proxy@^3.0.0:
|
|||
dependencies:
|
||||
harmony-reflect "^1.4.6"
|
||||
|
||||
idtoken-verifier@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/idtoken-verifier/-/idtoken-verifier-2.0.2.tgz#7fd1c64c435abf07e92f137e7ac538a758fdc399"
|
||||
integrity sha512-9UN83SKT9dtN3d7vNz3EMTqoaJi3D02Zg5XMqF6+bLrGL+Akbx4oj4SEWsgXtLF6cy46XrUcVzokFY+SWO+/MA==
|
||||
dependencies:
|
||||
base64-js "^1.3.0"
|
||||
crypto-js "^3.2.1"
|
||||
es6-promise "^4.2.8"
|
||||
jsbn "^1.1.0"
|
||||
unfetch "^4.1.0"
|
||||
url-join "^4.0.1"
|
||||
|
||||
ieee754@^1.1.4:
|
||||
version "1.1.12"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
|
||||
|
@ -6957,6 +7023,11 @@ js-base64@^2.1.9:
|
|||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03"
|
||||
integrity sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==
|
||||
|
||||
js-cookie@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
|
||||
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
|
||||
|
||||
js-levenshtein@^1.1.3:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||
|
@ -6980,6 +7051,11 @@ js-yaml@^3.13.1:
|
|||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsbn@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
|
||||
integrity sha1-sBMHyym2GKHtJux56RH4A8TaAEA=
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
|
@ -7639,7 +7715,7 @@ merge-stream@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||
|
||||
methods@~1.1.2:
|
||||
methods@^1.1.1, methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
||||
|
@ -7705,7 +7781,7 @@ mime@1.4.1:
|
|||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
|
||||
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
|
||||
|
||||
mime@1.6.0:
|
||||
mime@1.6.0, mime@^1.4.1:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
@ -9511,6 +9587,11 @@ qs@6.7.0:
|
|||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
qs@^6.5.1, qs@^6.7.0:
|
||||
version "6.9.1"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9"
|
||||
integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==
|
||||
|
||||
qs@^6.5.2, qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
|
@ -9838,6 +9919,14 @@ react-scrollbars-custom@^4.0.0-alpha.8:
|
|||
cnbuilder "^1.0.8"
|
||||
react-draggable "^3.2.1"
|
||||
|
||||
react-spring@^8.0.27:
|
||||
version "8.0.27"
|
||||
resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-8.0.27.tgz#97d4dee677f41e0b2adcb696f3839680a3aa356a"
|
||||
integrity sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
prop-types "^15.5.8"
|
||||
|
||||
react-test-renderer@^16.0.0-0:
|
||||
version "16.5.2"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.5.2.tgz#92e9d2c6f763b9821b2e0b22f994ee675068b5ae"
|
||||
|
@ -9906,6 +9995,19 @@ read-pkg@^3.0.0:
|
|||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^2.3.5:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^3.0.6:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.0.6.tgz#351302e4c68b5abd6a2ed55376a7f9a25be3057a"
|
||||
|
@ -11147,6 +11249,22 @@ stylehacks@^4.0.0:
|
|||
postcss "^7.0.0"
|
||||
postcss-selector-parser "^3.0.0"
|
||||
|
||||
superagent@^3.8.3:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128"
|
||||
integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==
|
||||
dependencies:
|
||||
component-emitter "^1.2.0"
|
||||
cookiejar "^2.1.0"
|
||||
debug "^3.1.0"
|
||||
extend "^3.0.0"
|
||||
form-data "^2.3.1"
|
||||
formidable "^1.2.0"
|
||||
methods "^1.1.1"
|
||||
mime "^1.4.1"
|
||||
qs "^6.5.1"
|
||||
readable-stream "^2.3.5"
|
||||
|
||||
supports-color@5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
|
||||
|
@ -11712,6 +11830,11 @@ underscore@~1.4.4:
|
|||
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"
|
||||
integrity sha1-YaajIBBiKvoHljvzJSA88SI51gQ=
|
||||
|
||||
unfetch@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db"
|
||||
integrity sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg==
|
||||
|
||||
unherit@^1.0.4:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.1.tgz#132748da3e88eab767e08fabfbb89c5e9d28628c"
|
||||
|
@ -11885,6 +12008,11 @@ urix@^0.1.0:
|
|||
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
|
||||
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
|
||||
|
||||
url-join@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
|
||||
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
|
||||
|
||||
url-parse@^1.4.3:
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
|
||||
|
@ -12336,6 +12464,11 @@ wide-align@^1.1.0:
|
|||
dependencies:
|
||||
string-width "^1.0.2 || 2"
|
||||
|
||||
winchan@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/winchan/-/winchan-0.2.2.tgz#6766917b88e5e1cb75f455ffc7cc13f51e5c834e"
|
||||
integrity sha512-pvN+IFAbRP74n/6mc6phNyCH8oVkzXsto4KCHPJ2AScniAnA1AmeLI03I2BzjePpaClGSI4GUMowzsD3qz5PRQ==
|
||||
|
||||
window-size@0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
|
||||
|
|
Loading…
Reference in New Issue