mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-02 16:38:14 +00:00
sign up page
This commit is contained in:
1 parent
f161655985
commit
bd515b3cb7
3 files changed
+171
-120
No files matched your search
@@ -133,18 +133,17 @@ nav {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
padding: 0.5rem 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:active {
|
||||
background-color: #fc6b68;
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
border-color: #ffffff12 !important;
|
||||
border-color: rgba($white, 0.12) !important;
|
||||
border: 1px solid;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 5px 20px rgba($black, 0.3);
|
||||
}
|
||||
|
||||
.nav-bg-dark {
|
||||
|
||||
@@ -174,6 +174,15 @@ code {
|
||||
}
|
||||
}
|
||||
|
||||
.form-text {
|
||||
color: lighten($primary, 5%);
|
||||
}
|
||||
|
||||
.alert {
|
||||
border-radius: 5px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
.form-control {
|
||||
padding-left: 2.375rem;
|
||||
@@ -190,7 +199,7 @@ code {
|
||||
display: block;
|
||||
width: 2.375rem;
|
||||
height: 2.375rem;
|
||||
line-height: 2.375rem;
|
||||
line-height: 2.6rem;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
color: #212529;
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import React, { useState } from 'react';
|
||||
import { CreateAccount, CreateAccountIsError, CreateAccountIsSuccess } from '@storyteller/components/src/api/user/CreateAccount';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { iconEmailField, iconPasswordField, iconUser } from '@storyteller/components/src/icons/SemanticIcons';
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
CreateAccount,
|
||||
CreateAccountIsError,
|
||||
CreateAccountIsSuccess,
|
||||
} from "@storyteller/components/src/api/user/CreateAccount";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link } from "react-router-dom";
|
||||
import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper";
|
||||
import {
|
||||
iconEmailField,
|
||||
iconPasswordField,
|
||||
iconUser,
|
||||
} from "@storyteller/components/src/icons/SemanticIcons";
|
||||
|
||||
enum FieldTriState {
|
||||
EMPTY_FALSE,
|
||||
@@ -12,27 +20,31 @@ enum FieldTriState {
|
||||
}
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
querySessionCallback : () => void,
|
||||
sessionWrapper: SessionWrapper;
|
||||
querySessionCallback: () => void;
|
||||
}
|
||||
|
||||
function SignupPage(props: Props) {
|
||||
|
||||
const [username, setUsername] = useState('');
|
||||
const [username, setUsername] = useState("");
|
||||
const [usernameValid, setUsernameValid] = useState(FieldTriState.EMPTY_FALSE);
|
||||
const [usernameInvalidReason, setUsernameInvalidReason] = useState('');
|
||||
const [usernameInvalidReason, setUsernameInvalidReason] = useState("");
|
||||
|
||||
const [email, setEmail] = useState('');
|
||||
const [email, setEmail] = useState("");
|
||||
const [emailValid, setEmailValid] = useState(FieldTriState.EMPTY_FALSE);
|
||||
const [emailInvalidReason, setEmailInvalidReason] = useState('');
|
||||
const [emailInvalidReason, setEmailInvalidReason] = useState("");
|
||||
|
||||
const [password, setPassword] = useState('');
|
||||
const [password, setPassword] = useState("");
|
||||
const [passwordValid, setPasswordValid] = useState(FieldTriState.EMPTY_FALSE);
|
||||
const [passwordInvalidReason, setPasswordInvalidReason] = useState('');
|
||||
const [passwordInvalidReason, setPasswordInvalidReason] = useState("");
|
||||
|
||||
const [passwordConfirmation, setPasswordConfirmation] = useState('');
|
||||
const [passwordConfirmationValid, setPasswordConfirmationValid] = useState(FieldTriState.EMPTY_FALSE);
|
||||
const [passwordConfirmationInvalidReason, setPasswordConfirmationInvalidReason] = useState('');
|
||||
const [passwordConfirmation, setPasswordConfirmation] = useState("");
|
||||
const [passwordConfirmationValid, setPasswordConfirmationValid] = useState(
|
||||
FieldTriState.EMPTY_FALSE
|
||||
);
|
||||
const [
|
||||
passwordConfirmationInvalidReason,
|
||||
setPasswordConfirmationInvalidReason,
|
||||
] = useState("");
|
||||
|
||||
const handleUsernameChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
ev.preventDefault();
|
||||
@@ -59,7 +71,7 @@ function SignupPage(props: Props) {
|
||||
setUsernameInvalidReason(usernameInvalidReason);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleEmailChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
ev.preventDefault();
|
||||
@@ -86,7 +98,7 @@ function SignupPage(props: Props) {
|
||||
setEmailInvalidReason(emailInvalidReason);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
ev.preventDefault();
|
||||
@@ -112,7 +124,6 @@ function SignupPage(props: Props) {
|
||||
} else {
|
||||
passwordConfirmationValid = FieldTriState.TRUE;
|
||||
passwordConfirmationInvalidReason = "";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,12 +131,14 @@ function SignupPage(props: Props) {
|
||||
setPasswordValid(passwordValid);
|
||||
setPasswordInvalidReason(passwordInvalidReason);
|
||||
setPasswordConfirmationValid(passwordConfirmationValid);
|
||||
setPasswordConfirmationInvalidReason(passwordConfirmationInvalidReason)
|
||||
setPasswordConfirmationInvalidReason(passwordConfirmationInvalidReason);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handlePasswordConfirmationChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
const handlePasswordConfirmationChange = (
|
||||
ev: React.FormEvent<HTMLInputElement>
|
||||
) => {
|
||||
ev.preventDefault();
|
||||
|
||||
const newPasswordConfirmation = (ev.target as HTMLInputElement).value;
|
||||
@@ -134,14 +147,12 @@ function SignupPage(props: Props) {
|
||||
let passwordConfirmationInvalidReason = "";
|
||||
|
||||
if (newPasswordConfirmation.length > 1) {
|
||||
|
||||
if (newPasswordConfirmation !== password) {
|
||||
passwordConfirmationValid = FieldTriState.FALSE;
|
||||
passwordConfirmationInvalidReason = "passwords do not match";
|
||||
} else {
|
||||
passwordConfirmationValid = FieldTriState.TRUE;
|
||||
passwordConfirmationInvalidReason = "";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,16 +161,20 @@ function SignupPage(props: Props) {
|
||||
setPasswordConfirmationInvalidReason(passwordConfirmationInvalidReason);
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleFormSubmit = async (ev: React.FormEvent<HTMLFormElement>) : Promise<boolean> => {
|
||||
const handleFormSubmit = async (
|
||||
ev: React.FormEvent<HTMLFormElement>
|
||||
): Promise<boolean> => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!usernameValid ||
|
||||
!emailValid ||
|
||||
!passwordValid ||
|
||||
!passwordConfirmationValid) {
|
||||
return false;
|
||||
if (
|
||||
!usernameValid ||
|
||||
!emailValid ||
|
||||
!passwordValid ||
|
||||
!passwordConfirmationValid
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const request = {
|
||||
@@ -172,32 +187,31 @@ function SignupPage(props: Props) {
|
||||
const response = await CreateAccount(request);
|
||||
|
||||
if (CreateAccountIsError(response)) {
|
||||
if ('email_address' in response.error_fields) {
|
||||
if ("email_address" in response.error_fields) {
|
||||
setEmailValid(FieldTriState.FALSE);
|
||||
setEmailInvalidReason(response.error_fields['email_address'] || "");
|
||||
setEmailInvalidReason(response.error_fields["email_address"] || "");
|
||||
}
|
||||
if ('username' in response.error_fields) {
|
||||
if ("username" in response.error_fields) {
|
||||
setUsernameValid(FieldTriState.FALSE);
|
||||
setUsernameInvalidReason(response.error_fields['username'] || "");
|
||||
setUsernameInvalidReason(response.error_fields["username"] || "");
|
||||
}
|
||||
} else if (CreateAccountIsSuccess(response)) {
|
||||
console.log('querying new session');
|
||||
console.log("querying new session");
|
||||
props.querySessionCallback();
|
||||
|
||||
// TODO: Switch to functional component.
|
||||
window.location.href = '/';
|
||||
window.location.href = "/";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (props.sessionWrapper.isLoggedIn()) {
|
||||
return <div>Invalid view for logged in users.</div>;
|
||||
}
|
||||
|
||||
let usernameInputClass = "input";
|
||||
let usernameHelpClass = "help";
|
||||
let usernameInputClass = "form-control";
|
||||
let usernameHelpClass = "form-text";
|
||||
switch (usernameValid) {
|
||||
case FieldTriState.EMPTY_FALSE:
|
||||
break;
|
||||
@@ -211,8 +225,8 @@ function SignupPage(props: Props) {
|
||||
break;
|
||||
}
|
||||
|
||||
let emailInputClass = "input";
|
||||
let emailHelpClass = "help";
|
||||
let emailInputClass = "form-control";
|
||||
let emailHelpClass = "form-text";
|
||||
switch (emailValid) {
|
||||
case FieldTriState.EMPTY_FALSE:
|
||||
break;
|
||||
@@ -226,8 +240,8 @@ function SignupPage(props: Props) {
|
||||
break;
|
||||
}
|
||||
|
||||
let passwordInputClass = "input";
|
||||
let passwordHelpClass = "help";
|
||||
let passwordInputClass = "form-control";
|
||||
let passwordHelpClass = "form-text";
|
||||
switch (passwordValid) {
|
||||
case FieldTriState.EMPTY_FALSE:
|
||||
break;
|
||||
@@ -241,8 +255,8 @@ function SignupPage(props: Props) {
|
||||
break;
|
||||
}
|
||||
|
||||
let passwordConfirmationInputClass = "input";
|
||||
let passwordConfirmationHelpClass = "help";
|
||||
let passwordConfirmationInputClass = "form-control";
|
||||
let passwordConfirmationHelpClass = "form-text";
|
||||
switch (passwordConfirmationValid) {
|
||||
case FieldTriState.EMPTY_FALSE:
|
||||
break;
|
||||
@@ -258,74 +272,103 @@ function SignupPage(props: Props) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title is-1"> Sign Up </h1>
|
||||
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<div className="field">
|
||||
<label className="label">Username</label>
|
||||
<div className="control has-icons-left has-icons-right">
|
||||
<input className={usernameInputClass} type="text" placeholder="Username" value={username} onChange={handleUsernameChange} />
|
||||
<span className="icon is-small is-left">
|
||||
<FontAwesomeIcon icon={iconUser} />
|
||||
</span>
|
||||
<div className="container-panel pb-5 pt-lg-5 my-lg-5 login-panel">
|
||||
<div className="panel p-3 p-lg-4 load-hidden mt-5 mt-lg-0">
|
||||
<h1 className="panel-title fw-bold ">Sign Up</h1>
|
||||
<div className="py-6">
|
||||
<form onSubmit={handleFormSubmit}>
|
||||
<div className="d-flex flex-column gap-4">
|
||||
<div>
|
||||
<label className="sub-title">Username</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={iconUser} />
|
||||
</span>
|
||||
<input
|
||||
className={usernameInputClass}
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={handleUsernameChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={usernameHelpClass}>{usernameInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">Email</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={iconEmailField} />
|
||||
</span>
|
||||
<input
|
||||
className={emailInputClass}
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={handleEmailChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={emailHelpClass}>{emailInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">Password</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={iconPasswordField} />
|
||||
</span>
|
||||
<input
|
||||
className={passwordInputClass}
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={handlePasswordChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={passwordHelpClass}>{passwordInvalidReason}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label className="sub-title">Password Confirmation</label>
|
||||
<div className="form-group input-icon">
|
||||
<span className="form-control-feedback">
|
||||
<FontAwesomeIcon icon={iconPasswordField} />
|
||||
</span>
|
||||
<input
|
||||
className={passwordConfirmationInputClass}
|
||||
type="password"
|
||||
placeholder="Password confirmation"
|
||||
value={passwordConfirmation}
|
||||
onChange={handlePasswordConfirmationChange}
|
||||
/>
|
||||
</div>
|
||||
<p className={passwordConfirmationHelpClass}>
|
||||
{passwordConfirmationInvalidReason}
|
||||
</p>
|
||||
</div>
|
||||
<div className="alert alert-warning mb-0">
|
||||
<strong>Remember your password!</strong> We don't have
|
||||
password reset currently, and it'll be a few more weeks before
|
||||
it's added (there are more important features to work on). If
|
||||
you lose your password, please let us know in Discord.
|
||||
</div>
|
||||
<button className="btn btn-primary btn-lg w-100 mt-2">
|
||||
Sign up
|
||||
</button>
|
||||
<p>
|
||||
Already have an account?
|
||||
<Link
|
||||
to="/login"
|
||||
className="button is-info is-large is-fullwidth is-inverted"
|
||||
>
|
||||
Log in instead.
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<p className={usernameHelpClass}>{usernameInvalidReason}</p>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">Email</label>
|
||||
<div className="control has-icons-left has-icons-right">
|
||||
<input className={emailInputClass} type="email" placeholder="Email" value={email} onChange={handleEmailChange} />
|
||||
<span className="icon is-small is-left">
|
||||
<FontAwesomeIcon icon={iconEmailField} />
|
||||
</span>
|
||||
</div>
|
||||
<p className={emailHelpClass}>{emailInvalidReason}</p>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">Password</label>
|
||||
<div className="control has-icons-left has-icons-right">
|
||||
<input className={passwordInputClass} type="password" placeholder="Password" value={password} onChange={handlePasswordChange} />
|
||||
<span className="icon is-small is-left">
|
||||
<FontAwesomeIcon icon={iconPasswordField} />
|
||||
</span>
|
||||
</div>
|
||||
<p className={passwordHelpClass}>{passwordInvalidReason}</p>
|
||||
</div>
|
||||
|
||||
<div className="field">
|
||||
<label className="label">Password Confirmation</label>
|
||||
<div className="control has-icons-left has-icons-right">
|
||||
<input className={passwordConfirmationInputClass} type="password" placeholder="Password confirmation" value={passwordConfirmation} onChange={handlePasswordConfirmationChange} />
|
||||
<span className="icon is-small is-left">
|
||||
<FontAwesomeIcon icon={iconPasswordField} />
|
||||
</span>
|
||||
</div>
|
||||
<p className={passwordConfirmationHelpClass}>
|
||||
{passwordConfirmationInvalidReason}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div className="notification is-warning">
|
||||
<strong>Remember your password!</strong> We don't have password reset currently, and it'll be a
|
||||
few more weeks before it's added (there are more important features to work on). If you lose your
|
||||
password, please let us know in Discord.
|
||||
</div>
|
||||
|
||||
<button className="button is-link is-large is-fullwidth">Sign up</button>
|
||||
</form>
|
||||
|
||||
<br />
|
||||
|
||||
<Link to="/login"
|
||||
className="button is-info is-large is-fullwidth is-inverted"
|
||||
>Already have an account? Log in instead!</Link>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { SignupPage };
|
||||
export { SignupPage };
|
||||
Reference in new issue
Block a user