mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 19:44:15 +00:00
added recovery ui
This commit is contained in:
parent
5b6d36744c
commit
be0c61a028
@ -15,11 +15,13 @@ const stepsOnError = {
|
|||||||
|
|
||||||
const stepsValidations = {
|
const stepsValidations = {
|
||||||
username: async (state) => {
|
username: async (state) => {
|
||||||
const check = await AuthModel.usernameValidation(state.username).catch((err) => {
|
const check = await AuthModel.usernameValidation(state.username).catch(
|
||||||
|
(err) => {
|
||||||
return {
|
return {
|
||||||
exists: false
|
exists: false,
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return check.exists
|
return check.exists
|
||||||
},
|
},
|
||||||
@ -32,7 +34,7 @@ const phasesToSteps = {
|
|||||||
|
|
||||||
class Login extends React.Component {
|
class Login extends React.Component {
|
||||||
static pageStatement = {
|
static pageStatement = {
|
||||||
bottomBarAllowed: false
|
bottomBarAllowed: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -66,7 +68,7 @@ class Login extends React.Component {
|
|||||||
if (error.response.data) {
|
if (error.response.data) {
|
||||||
if (error.response.data.violation) {
|
if (error.response.data.violation) {
|
||||||
return this.setState({
|
return this.setState({
|
||||||
forbidden: error.response.data.violation
|
forbidden: error.response.data.violation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +103,7 @@ class Login extends React.Component {
|
|||||||
|
|
||||||
if (typeof this.props.close === "function") {
|
if (typeof this.props.close === "function") {
|
||||||
await this.props.close({
|
await this.props.close({
|
||||||
unlock: true
|
unlock: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +124,7 @@ class Login extends React.Component {
|
|||||||
try {
|
try {
|
||||||
await AuthModel.activateAccount(
|
await AuthModel.activateAccount(
|
||||||
this.state.activation.user_id,
|
this.state.activation.user_id,
|
||||||
this.state.activation.code
|
this.state.activation.code,
|
||||||
)
|
)
|
||||||
|
|
||||||
this.handleFinish()
|
this.handleFinish()
|
||||||
@ -130,8 +132,8 @@ class Login extends React.Component {
|
|||||||
this.setState({
|
this.setState({
|
||||||
activation: {
|
activation: {
|
||||||
...this.state.activation,
|
...this.state.activation,
|
||||||
error: error
|
error: error,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@ -145,8 +147,9 @@ class Login extends React.Component {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const rensendObj = await AuthModel.resendActivationCode(activationObj.user_id)
|
const rensendObj = await AuthModel.resendActivationCode(
|
||||||
.catch((error) => {
|
activationObj.user_id,
|
||||||
|
).catch((error) => {
|
||||||
app.message.info(`Please try again later...`)
|
app.message.info(`Please try again later...`)
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
@ -170,7 +173,7 @@ class Login extends React.Component {
|
|||||||
this.props.close()
|
this.props.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
app.location.push("/apr")
|
app.location.push("/auth?key=recover")
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLoading = (to) => {
|
toggleLoading = (to) => {
|
||||||
@ -179,19 +182,19 @@ class Login extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: to
|
loading: to,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
clearError = () => {
|
clearError = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
error: null
|
error: null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onError = (error) => {
|
onError = (error) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
error: error
|
error: error,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,15 +208,15 @@ class Login extends React.Component {
|
|||||||
this.formRef.current.setFields([
|
this.formRef.current.setFields([
|
||||||
{
|
{
|
||||||
name: input,
|
name: input,
|
||||||
errors: []
|
errors: [],
|
||||||
}
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
loginInputs: {
|
loginInputs: {
|
||||||
...this.state.loginInputs,
|
...this.state.loginInputs,
|
||||||
[input]: value
|
[input]: value,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +234,7 @@ class Login extends React.Component {
|
|||||||
this.formRef.current.setFields([
|
this.formRef.current.setFields([
|
||||||
{
|
{
|
||||||
name: phase,
|
name: phase,
|
||||||
errors: [stepsOnError[phase]]
|
errors: [stepsOnError[phase]],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
@ -246,7 +249,7 @@ class Login extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: to
|
phase: to,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,10 +282,14 @@ class Login extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.state.forbidden) {
|
if (this.state.forbidden) {
|
||||||
return <div className="login_wrapper">
|
return (
|
||||||
|
<div className="login_wrapper">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<h1>Access denied</h1>
|
<h1>Access denied</h1>
|
||||||
<h3>Your account has been disabled due a violation to our terms of service</h3>
|
<h3>
|
||||||
|
Your account has been disabled due a violation to
|
||||||
|
our terms of service
|
||||||
|
</h3>
|
||||||
|
|
||||||
<p>Here is a detailed description of the violation</p>
|
<p>Here is a detailed description of the violation</p>
|
||||||
|
|
||||||
@ -290,33 +297,41 @@ class Login extends React.Component {
|
|||||||
{this.state.forbidden.reason}
|
{this.state.forbidden.reason}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>If you think this is an error, or you want to apeel this decision please contact our support</p>
|
<p>
|
||||||
|
If you think this is an error, or you want to apeel
|
||||||
|
this decision please contact our support
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.activation) {
|
if (this.state.activation) {
|
||||||
return <div className="login_wrapper">
|
return (
|
||||||
|
<div className="login_wrapper">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<h1>Activate your Account</h1>
|
<h1>Activate your Account</h1>
|
||||||
<p>We have sent you an email with a code that you need to enter below in order to activate your account.</p>
|
<p>
|
||||||
|
We have sent you an email with a code that you need
|
||||||
|
to enter below in order to activate your account.
|
||||||
|
</p>
|
||||||
|
|
||||||
<antd.Input.OTP
|
<antd.Input.OTP
|
||||||
length={6}
|
length={6}
|
||||||
onChange={(code) => this.setState({
|
onChange={(code) =>
|
||||||
|
this.setState({
|
||||||
activation: {
|
activation: {
|
||||||
...this.state.activation,
|
...this.state.activation,
|
||||||
code: code,
|
code: code,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="resend">
|
<div className="resend">
|
||||||
{
|
{this.state.activation.resended && (
|
||||||
this.state.activation.resended && <antd.Alert
|
<antd.Alert message={`Mail resended`} />
|
||||||
message={`Mail resended`}
|
)}
|
||||||
/>
|
|
||||||
}
|
|
||||||
<a
|
<a
|
||||||
href="#"
|
href="#"
|
||||||
onClick={this.onClickResendActivationCode}
|
onClick={this.onClickResendActivationCode}
|
||||||
@ -325,30 +340,29 @@ class Login extends React.Component {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{this.state.activation.error && (
|
||||||
|
<div className="field-error">
|
||||||
{
|
{
|
||||||
this.state.activation.error && <div className="field-error">
|
this.state.activation.error.response.data
|
||||||
{this.state.activation.error.response.data.error}
|
.error
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<antd.Button
|
<antd.Button onClick={this.onClickActivateAccount}>
|
||||||
onClick={this.onClickActivateAccount}
|
|
||||||
>
|
|
||||||
Activate
|
Activate
|
||||||
</antd.Button>
|
</antd.Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="login_wrapper">
|
return (
|
||||||
|
<div className="login_wrapper">
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h1>
|
<h1>Sign in</h1>
|
||||||
Sign in
|
<h3>To continue to {config.app.siteName}</h3>
|
||||||
</h1>
|
|
||||||
<h3>
|
|
||||||
To continue to {config.app.siteName}
|
|
||||||
</h3>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<antd.Form
|
<antd.Form
|
||||||
@ -360,14 +374,18 @@ class Login extends React.Component {
|
|||||||
onFinish={this.handleFinish}
|
onFinish={this.handleFinish}
|
||||||
ref={this.formRef}
|
ref={this.formRef}
|
||||||
>
|
>
|
||||||
<antd.Form.Item
|
<antd.Form.Item name="username" className="field">
|
||||||
name="username"
|
<span>
|
||||||
className="field"
|
<Icons.FiMail /> Username or Email
|
||||||
>
|
</span>
|
||||||
<span><Icons.FiMail /> Username or Email</span>
|
|
||||||
<antd.Input
|
<antd.Input
|
||||||
placeholder="myusername / myemail@example.com"
|
placeholder="myusername / myemail@example.com"
|
||||||
onChange={(e) => this.onUpdateInput("username", e.target.value)}
|
onChange={(e) =>
|
||||||
|
this.onUpdateInput(
|
||||||
|
"username",
|
||||||
|
e.target.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
onPressEnter={this.nextStep}
|
onPressEnter={this.nextStep}
|
||||||
disabled={this.state.phase !== 0}
|
disabled={this.state.phase !== 0}
|
||||||
autoFocus
|
autoFocus
|
||||||
@ -376,60 +394,71 @@ class Login extends React.Component {
|
|||||||
|
|
||||||
<antd.Form.Item
|
<antd.Form.Item
|
||||||
name="password"
|
name="password"
|
||||||
className={classnames(
|
className={classnames("field", {
|
||||||
"field",
|
|
||||||
{
|
|
||||||
["hidden"]: this.state.phase !== 1,
|
["hidden"]: this.state.phase !== 1,
|
||||||
}
|
})}
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<span><Icons.FiLock /> Password</span>
|
<span>
|
||||||
|
<Icons.FiLock /> Password
|
||||||
|
</span>
|
||||||
<antd.Input.Password
|
<antd.Input.Password
|
||||||
//placeholder="********"
|
//placeholder="********"
|
||||||
onChange={(e) => this.onUpdateInput("password", e.target.value)}
|
onChange={(e) =>
|
||||||
|
this.onUpdateInput(
|
||||||
|
"password",
|
||||||
|
e.target.value,
|
||||||
|
)
|
||||||
|
}
|
||||||
onPressEnter={this.nextStep}
|
onPressEnter={this.nextStep}
|
||||||
/>
|
/>
|
||||||
</antd.Form.Item>
|
</antd.Form.Item>
|
||||||
|
|
||||||
<antd.Form.Item
|
<antd.Form.Item
|
||||||
name="mfa_code"
|
name="mfa_code"
|
||||||
className={classnames(
|
className={classnames("field", {
|
||||||
"field",
|
|
||||||
{
|
|
||||||
["hidden"]: !this.state.mfa_required,
|
["hidden"]: !this.state.mfa_required,
|
||||||
}
|
})}
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<span><Icons.FiLock /> Verification Code</span>
|
<span>
|
||||||
|
<Icons.FiLock /> Verification Code
|
||||||
|
</span>
|
||||||
|
|
||||||
{
|
{this.state.mfa_required && (
|
||||||
this.state.mfa_required && <>
|
<>
|
||||||
<p>We send a verification code to [{this.state.mfa_required.sended_to}]</p>
|
<p>
|
||||||
|
We send a verification code to [
|
||||||
|
{this.state.mfa_required.sended_to}]
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Didn't receive the code? <a onClick={this.handleFinish}>Resend</a>
|
Didn't receive the code?{" "}
|
||||||
|
<a onClick={this.handleFinish}>
|
||||||
|
Resend
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<antd.Input.OTP
|
<antd.Input.OTP
|
||||||
length={4}
|
length={4}
|
||||||
formatter={(str) => str.toUpperCase()}
|
formatter={(str) => str.toUpperCase()}
|
||||||
onChange={(code) => this.onUpdateInput("mfa_code", code)}
|
onChange={(code) =>
|
||||||
|
this.onUpdateInput("mfa_code", code)
|
||||||
|
}
|
||||||
onPressEnter={this.nextStep}
|
onPressEnter={this.nextStep}
|
||||||
/>
|
/>
|
||||||
</antd.Form.Item>
|
</antd.Form.Item>
|
||||||
</antd.Form>
|
</antd.Form>
|
||||||
|
|
||||||
<div className="component-row">
|
<div className="component-row">
|
||||||
{
|
{this.state.phase > 0 && (
|
||||||
this.state.phase > 0 && <antd.Button
|
<antd.Button
|
||||||
onClick={this.prevStep}
|
onClick={this.prevStep}
|
||||||
disabled={this.state.loading}
|
disabled={this.state.loading}
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</antd.Button>
|
</antd.Button>
|
||||||
}
|
)}
|
||||||
<antd.Button
|
<antd.Button
|
||||||
onClick={this.nextStep}
|
onClick={this.nextStep}
|
||||||
disabled={!this.canNext() || this.state.loading}
|
disabled={!this.canNext() || this.state.loading}
|
||||||
@ -439,17 +468,16 @@ class Login extends React.Component {
|
|||||||
</antd.Button>
|
</antd.Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{this.state.error && (
|
||||||
this.state.error && <div className="field-error">
|
<div className="field-error">{this.state.error}</div>
|
||||||
{this.state.error}
|
)}
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className="field" onClick={this.onClickForgotPassword}>
|
<div className="field" onClick={this.onClickForgotPassword}>
|
||||||
<a>Forgot your password?</a>
|
<a>Forgot your password?</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
149
packages/app/src/pages/auth/forms/recovery/index.jsx
Normal file
149
packages/app/src/pages/auth/forms/recovery/index.jsx
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import React from "react"
|
||||||
|
import { Input } from "antd"
|
||||||
|
|
||||||
|
import FormWithSteps from "@components/FormWithSteps"
|
||||||
|
import AuthModel from "@models/auth"
|
||||||
|
|
||||||
|
const Steps = [
|
||||||
|
{
|
||||||
|
id: "email_input",
|
||||||
|
render: ({ updateState, values }) => {
|
||||||
|
function onChangeInput(e) {
|
||||||
|
updateState("account", e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
First enter your account or email address to find your
|
||||||
|
associated account.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
placeholder="@username or email"
|
||||||
|
value={values.account}
|
||||||
|
onChange={onChangeInput}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
validate: (values) => {
|
||||||
|
return values.account && values.account.length > 3
|
||||||
|
},
|
||||||
|
onNext: async ({ values, updateState, setError }) => {
|
||||||
|
try {
|
||||||
|
const recoverSession = await AuthModel.recoverPassword(
|
||||||
|
values.account,
|
||||||
|
)
|
||||||
|
|
||||||
|
updateState("recoverSession", recoverSession)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.response.data)
|
||||||
|
setError(error.response.data.error)
|
||||||
|
|
||||||
|
return {
|
||||||
|
cancel: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "new_password",
|
||||||
|
render: ({ updateState, values }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>Enter a new password for your account.</p>
|
||||||
|
|
||||||
|
<Input.Password
|
||||||
|
placeholder="New Password"
|
||||||
|
value={values.new_password}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateState("new_password", e.target.value)
|
||||||
|
}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
validate: (values) => {
|
||||||
|
if (!values.new_password) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.new_password.length >= 8
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "otp_input",
|
||||||
|
render: ({ updateState, values }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
We've sent you a code to your email [
|
||||||
|
{values.recoverSession.email}]
|
||||||
|
</p>
|
||||||
|
<p>Expires in {values.recoverSession.expires_in} minutes</p>
|
||||||
|
|
||||||
|
<Input.OTP
|
||||||
|
length={values.recoverSession.code_length}
|
||||||
|
onChange={(value) => updateState("otp", value)}
|
||||||
|
value={values.otp}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
validate: (values) => {
|
||||||
|
if (!values.otp) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.otp.length === values.recoverSession.code_length
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const OnFinish = async ({ values, setError }) => {
|
||||||
|
try {
|
||||||
|
const result = await AuthModel.changePassword({
|
||||||
|
newPassword: values.new_password,
|
||||||
|
code: values.otp,
|
||||||
|
verificationToken: values.recoverSession.verificationToken,
|
||||||
|
})
|
||||||
|
|
||||||
|
app.message.info("Password changed successfully")
|
||||||
|
app.navigation.goAuth()
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
setError(error.message)
|
||||||
|
|
||||||
|
return {
|
||||||
|
cancel: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
return (
|
||||||
|
<div className="steped-form-header">
|
||||||
|
<h1>Account Recovery</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const RecoveryPage = (props) => {
|
||||||
|
return (
|
||||||
|
<FormWithSteps
|
||||||
|
header={Header}
|
||||||
|
steps={Steps}
|
||||||
|
onCancel={() => {
|
||||||
|
props.setActiveKey("selector")
|
||||||
|
}}
|
||||||
|
onFinish={OnFinish}
|
||||||
|
cancelable
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RecoveryPage
|
@ -13,12 +13,7 @@ import PasswordStep from "./steps/password"
|
|||||||
import EmailStep from "./steps/email"
|
import EmailStep from "./steps/email"
|
||||||
import TOSStep from "./steps/tos"
|
import TOSStep from "./steps/tos"
|
||||||
|
|
||||||
const steps = [
|
const steps = [UsernameStep, PasswordStep, EmailStep, TOSStep]
|
||||||
UsernameStep,
|
|
||||||
PasswordStep,
|
|
||||||
EmailStep,
|
|
||||||
TOSStep,
|
|
||||||
]
|
|
||||||
|
|
||||||
const RegisterForm = (props) => {
|
const RegisterForm = (props) => {
|
||||||
const [finishing, setFinishing] = React.useState(false)
|
const [finishing, setFinishing] = React.useState(false)
|
||||||
@ -73,10 +68,11 @@ const RegisterForm = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateStepValue = (value) => setStepsValues((prev) => {
|
const updateStepValue = (value) =>
|
||||||
|
setStepsValues((prev) => {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[currentStepData.key]: value
|
[currentStepData.key]: value,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -100,115 +96,104 @@ const RegisterForm = (props) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div
|
return (
|
||||||
className={classnames(
|
<div
|
||||||
"register_form",
|
className={classnames("register_form", {
|
||||||
{
|
["welcome_step"]: step === 0 && !finishing,
|
||||||
["welcome_step"]: step === 0 && !finishing
|
})}
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<div className="register_form_header-text">
|
<div className="register_form_header-text">
|
||||||
{
|
{!finishSuccess && !finishing && step === 0 && (
|
||||||
!finishSuccess && !finishing && step === 0 && <>
|
<>
|
||||||
<h1>👋 Hi! Nice to meet you</h1>
|
<h1>👋 Hi! Nice to meet you</h1>
|
||||||
<p>Tell us some basic information to get started creating your account.</p>
|
<p>
|
||||||
|
Tell us some basic information to get started
|
||||||
|
creating your account.
|
||||||
|
</p>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{
|
{!finishSuccess && !finishing && step > 0 && (
|
||||||
!finishSuccess && !finishing && step > 0 && <>
|
<>
|
||||||
<h1>
|
<h1>
|
||||||
{
|
{currentStepData?.icon &&
|
||||||
currentStepData?.icon && createIconRender(currentStepData.icon)
|
createIconRender(currentStepData.icon)}
|
||||||
}
|
|
||||||
|
|
||||||
{currentStepData?.title}
|
{currentStepData?.title}
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
{
|
{typeof currentStepData?.description === "function"
|
||||||
typeof currentStepData?.description === "function" ?
|
? currentStepData?.description()
|
||||||
currentStepData?.description() : currentStepData.description
|
: currentStepData.description}
|
||||||
}
|
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{
|
{!finishSuccess &&
|
||||||
!finishSuccess && !finishing && step > 0 && React.createElement(currentStepData.content, {
|
!finishing &&
|
||||||
|
step > 0 &&
|
||||||
|
React.createElement(currentStepData.content, {
|
||||||
onPressEnter: nextStep,
|
onPressEnter: nextStep,
|
||||||
currentValue: stepsValues[currentStepData.key],
|
currentValue: stepsValues[currentStepData.key],
|
||||||
updateValue: updateStepValue,
|
updateValue: updateStepValue,
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{finishing && (
|
||||||
finishing && <div className="register_form_creating">
|
<div className="register_form_creating">
|
||||||
<Icons.LoadingOutlined />
|
<Icons.LoadingOutlined />
|
||||||
<h1>
|
<h1>Creating your account</h1>
|
||||||
Creating your account
|
|
||||||
</h1>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{
|
{finishSuccess && (
|
||||||
finishSuccess && <div className="register_form_success">
|
<div className="register_form_success">
|
||||||
<Icons.CheckCircleOutlined />
|
<Icons.CheckCircleOutlined />
|
||||||
<h1>
|
<h1>Welcome abord!</h1>
|
||||||
Welcome abord!
|
|
||||||
</h1>
|
|
||||||
<p>
|
<p>
|
||||||
One last step, we need you to login with your new account.
|
One last step, we need you to login with your new
|
||||||
|
account.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<antd.Button
|
<antd.Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => props.changeStage(0)}
|
onClick={() => props.setActiveKey("selector")}
|
||||||
>
|
>
|
||||||
Go to login
|
Go to login
|
||||||
</antd.Button>
|
</antd.Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
|
|
||||||
{
|
{finishError && (
|
||||||
finishError && <antd.Alert
|
<antd.Alert type="error" message={finishError.message} />
|
||||||
type="error"
|
)}
|
||||||
message={finishError.message}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{!finishSuccess && !finishing && (
|
||||||
!finishSuccess && !finishing && <div className="register_form_actions">
|
<div className="register_form_actions">
|
||||||
{
|
{step === 0 && (
|
||||||
step === 0 &&
|
|
||||||
<antd.Button
|
<antd.Button
|
||||||
onClick={() => props.changeStage(0)}
|
onClick={() => props.setActiveKey("selector")}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</antd.Button>
|
</antd.Button>
|
||||||
}
|
)}
|
||||||
{
|
{step > 0 && (
|
||||||
step > 0 &&
|
<antd.Button onClick={() => prevStep()}>
|
||||||
<antd.Button
|
|
||||||
onClick={() => prevStep()}
|
|
||||||
>
|
|
||||||
Back
|
Back
|
||||||
</antd.Button>
|
</antd.Button>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<antd.Button
|
<antd.Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => nextStep()}
|
onClick={() => nextStep()}
|
||||||
disabled={!canNextStep()}
|
disabled={!canNextStep()}
|
||||||
>
|
>
|
||||||
{
|
{step === steps.length ? "Finish" : "Next"}
|
||||||
step === steps.length ? "Finish" : "Next"
|
|
||||||
}
|
|
||||||
</antd.Button>
|
</antd.Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RegisterForm
|
export default RegisterForm
|
@ -4,31 +4,32 @@ import config from "@config"
|
|||||||
import { Icons } from "@components/Icons"
|
import { Icons } from "@components/Icons"
|
||||||
|
|
||||||
const MainSelector = (props) => {
|
const MainSelector = (props) => {
|
||||||
const {
|
return (
|
||||||
onClickLogin,
|
<>
|
||||||
onClickRegister,
|
|
||||||
} = props
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<div className="content_header">
|
<div className="content_header">
|
||||||
<img src={config.logo.alt} className="logo" />
|
<img src={config.logo.alt} className="logo" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
{
|
{app.userData && (
|
||||||
app.userData && <antd.Button
|
<antd.Button
|
||||||
type="default"
|
type="default"
|
||||||
size="large"
|
size="large"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
app.navigation.goMain()
|
app.navigation.goMain()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<antd.Avatar size={23} shape="square" src={app.userData.avatar} /> Continue as {app.userData.username}
|
<antd.Avatar
|
||||||
|
size={23}
|
||||||
|
shape="square"
|
||||||
|
src={app.userData.avatar}
|
||||||
|
/>{" "}
|
||||||
|
Continue as {app.userData.username}
|
||||||
</antd.Button>
|
</antd.Button>
|
||||||
}
|
)}
|
||||||
|
|
||||||
<antd.Button
|
<antd.Button
|
||||||
onClick={onClickLogin}
|
onClick={() => app.controls.openLoginForm()}
|
||||||
icon={<Icons.FiLogIn />}
|
icon={<Icons.FiLogIn />}
|
||||||
type="primary"
|
type="primary"
|
||||||
>
|
>
|
||||||
@ -36,7 +37,7 @@ const MainSelector = (props) => {
|
|||||||
</antd.Button>
|
</antd.Button>
|
||||||
|
|
||||||
<antd.Button
|
<antd.Button
|
||||||
onClick={onClickLogin}
|
onClick={() => app.controls.openLoginForm()}
|
||||||
icon={<Icons.FiLogIn />}
|
icon={<Icons.FiLogIn />}
|
||||||
type="primary"
|
type="primary"
|
||||||
disabled
|
disabled
|
||||||
@ -47,19 +48,19 @@ const MainSelector = (props) => {
|
|||||||
<h4>Or create a new account</h4>
|
<h4>Or create a new account</h4>
|
||||||
|
|
||||||
<antd.Button
|
<antd.Button
|
||||||
onClick={onClickRegister}
|
onClick={() => props.setActiveKey("register")}
|
||||||
icon={<Icons.FiUserPlus />}
|
icon={<Icons.FiUserPlus />}
|
||||||
type="primary"
|
type="primary"
|
||||||
>
|
>
|
||||||
Create a Comty™ Account
|
Create a Comty™ Account
|
||||||
</antd.Button>
|
</antd.Button>
|
||||||
|
|
||||||
<p style={{ display: "inline" }}>
|
<a onClick={() => props.setActiveKey("recovery")}>
|
||||||
<Icons.FiInfo />
|
I need help to recover my account
|
||||||
Registering a new account accepts the <a onClick={() => app.location.push("/terms")}>Terms and Conditions</a> and <a onClick={() => app.location.push("/privacy")}>Privacy policy</a> for the services provided by {config.author}
|
</a>
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MainSelector
|
export default MainSelector
|
@ -1,14 +1,17 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
|
|
||||||
import useRandomFeaturedWallpaperUrl from "@hooks/useRandomFeaturedWallpaperUrl"
|
import useRandomFeaturedWallpaperUrl from "@hooks/useRandomFeaturedWallpaperUrl"
|
||||||
|
import useUrlQueryActiveKey from "@hooks/useUrlQueryActiveKey"
|
||||||
|
|
||||||
import RegisterForm from "./forms/register"
|
import RegisterForm from "./forms/register"
|
||||||
import MainSelector from "./forms/selector"
|
import MainSelector from "./forms/selector"
|
||||||
|
import RecoveryForm from "./forms/recovery"
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
const GradientSVG = () => {
|
const GradientSVG = () => {
|
||||||
return <svg height="100%" width="100%">
|
return (
|
||||||
|
<svg height="100%" width="100%">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="0" x1="0" y1="0.5" x2="1" y2="0.5">
|
<linearGradient id="0" x1="0" y1="0.5" x2="1" y2="0.5">
|
||||||
<stop offset="0%" stop-color="rgba(225, 0, 209, 0.1)" />
|
<stop offset="0%" stop-color="rgba(225, 0, 209, 0.1)" />
|
||||||
@ -16,7 +19,10 @@ const GradientSVG = () => {
|
|||||||
<stop offset="50%" stop-color="rgba(240, 0, 154, 0.05)" />
|
<stop offset="50%" stop-color="rgba(240, 0, 154, 0.05)" />
|
||||||
<stop offset="100%" stop-color="rgba(255, 0, 0, 0)" />
|
<stop offset="100%" stop-color="rgba(255, 0, 0, 0)" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<radialGradient id="1" gradientTransform="translate(-0.81 -0.5) scale(2, 1.2)">
|
<radialGradient
|
||||||
|
id="1"
|
||||||
|
gradientTransform="translate(-0.81 -0.5) scale(2, 1.2)"
|
||||||
|
>
|
||||||
<stop offset="0%" stop-color="rgba(255, 96, 100, 0.2)" />
|
<stop offset="0%" stop-color="rgba(255, 96, 100, 0.2)" />
|
||||||
<stop offset="20%" stop-color="rgba(255, 96, 100, 0.16)" />
|
<stop offset="20%" stop-color="rgba(255, 96, 100, 0.16)" />
|
||||||
<stop offset="40%" stop-color="rgba(255, 96, 100, 0.12)" />
|
<stop offset="40%" stop-color="rgba(255, 96, 100, 0.12)" />
|
||||||
@ -27,30 +33,23 @@ const GradientSVG = () => {
|
|||||||
<rect fill="url(#0)" height="100%" width="100%" />
|
<rect fill="url(#0)" height="100%" width="100%" />
|
||||||
<rect fill="url(#1)" height="100%" width="100%" />
|
<rect fill="url(#1)" height="100%" width="100%" />
|
||||||
</svg>
|
</svg>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const stagesToComponents = {
|
const keyToComponents = {
|
||||||
0: MainSelector,
|
selector: MainSelector,
|
||||||
2: RegisterForm
|
register: RegisterForm,
|
||||||
|
recovery: RecoveryForm,
|
||||||
}
|
}
|
||||||
|
|
||||||
const AuthPage = (props) => {
|
const AuthPage = (props) => {
|
||||||
const [stage, setStage] = React.useState(0)
|
const [activeKey, setActiveKey] = useUrlQueryActiveKey({
|
||||||
|
defaultKey: "selector",
|
||||||
|
})
|
||||||
const randomWallpaperURL = useRandomFeaturedWallpaperUrl()
|
const randomWallpaperURL = useRandomFeaturedWallpaperUrl()
|
||||||
|
|
||||||
function changeStage(nextStage) {
|
return (
|
||||||
setStage(nextStage)
|
<div className="login-page">
|
||||||
}
|
|
||||||
|
|
||||||
const onClickLogin = () => {
|
|
||||||
app.controls.openLoginForm()
|
|
||||||
}
|
|
||||||
|
|
||||||
const onClickRegister = () => {
|
|
||||||
changeStage(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="login-page">
|
|
||||||
<div className="background">
|
<div className="background">
|
||||||
<GradientSVG />
|
<GradientSVG />
|
||||||
</div>
|
</div>
|
||||||
@ -59,22 +58,25 @@ const AuthPage = (props) => {
|
|||||||
<div
|
<div
|
||||||
className="wrapper_background"
|
className="wrapper_background"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: randomWallpaperURL ? `url(${randomWallpaperURL})` : null,
|
backgroundImage: randomWallpaperURL
|
||||||
animation: randomWallpaperURL ? "opacityIn 1s" : null
|
? `url(${randomWallpaperURL})`
|
||||||
|
: null,
|
||||||
|
animation: randomWallpaperURL ? "opacityIn 1s" : null,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="content">
|
<div className="content">
|
||||||
|
{React.createElement(
|
||||||
|
keyToComponents[activeKey] ??
|
||||||
|
keyToComponents["selector"],
|
||||||
{
|
{
|
||||||
React.createElement(stagesToComponents[stage] ?? stagesToComponents[0], {
|
setActiveKey: setActiveKey,
|
||||||
onClickLogin,
|
},
|
||||||
onClickRegister,
|
)}
|
||||||
changeStage,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AuthPage
|
export default AuthPage
|
@ -1,11 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
|
|
||||||
import "./index.less"
|
|
||||||
|
|
||||||
const AccountPasswordRecovery = () => {
|
|
||||||
return <div className="password_recover">
|
|
||||||
<h1>Account Password Recovery</h1>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AccountPasswordRecovery
|
|
@ -1,3 +0,0 @@
|
|||||||
.password_recover {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user