mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
login mechanism
This commit is contained in:
parent
6168841f78
commit
9528e7f699
27 changed files with 528 additions and 96 deletions
|
@ -1,13 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import classnames from 'classnames';
|
||||
// import * as AutoResponsive from 'autoresponsive-react';
|
||||
// const ReactGridLayout = require('react-grid-layout');
|
||||
// import 'react-grid-layout/css/styles.css';
|
||||
// import 'react-resizable/css/styles.css';
|
||||
|
||||
const style = require('./style.scss');
|
||||
// const Packery = require('react-packery-component')(React);
|
||||
// http://37.192.131.144/hero/photos/photo-20120825-1532512.jpg
|
||||
|
||||
export const TestGrid = () => (
|
||||
<div className={style.grid_test}>
|
||||
|
@ -23,50 +17,3 @@ export const TestGrid = () => (
|
|||
<div className={classnames([style.cell, style.vert_1, style.hor_1])} key="j" />
|
||||
</div>
|
||||
);
|
||||
|
||||
// export const TestGrid = () => (
|
||||
// <ReactGridLayout
|
||||
// className="layout"
|
||||
// cols={4}
|
||||
// rowHeight={256}
|
||||
// width={1024 + 256}
|
||||
// layout={layout}
|
||||
// margin={[0, 0]}
|
||||
// compactType="vertical"
|
||||
// verticalCompact
|
||||
// >
|
||||
// <div className={style.cell} key="a" />
|
||||
// <div className={style.cell} key="b" />
|
||||
// <div className={style.cell} key="c" />
|
||||
// <div className={style.cell} key="d" />
|
||||
// <div className={style.cell} key="e" />
|
||||
// <div className={style.cell} key="f" />
|
||||
// <div className={style.cell} key="g" />
|
||||
// </ReactGridLayout>
|
||||
// );
|
||||
|
||||
// export const TestGrid = () => (
|
||||
// <AutoResponsive
|
||||
// itemMargin={0}
|
||||
// containerWidth={1024 + 256}
|
||||
// itemClassName={style.cell}
|
||||
// gridWidth={256}
|
||||
// transitionDuration={0}
|
||||
// >
|
||||
// <div style={{ width: 256 * 4, height: 256 * 2 }} className={style.cell} key="a" />
|
||||
// <div style={{ width: 256, height: 256 * 2 }} className={style.cell} key="b" />
|
||||
// <div style={{ width: 256, height: 256 }} className={style.cell} key="c" />
|
||||
// <div style={{ width: 256, height: 256 }} className={style.cell} key="d" />
|
||||
// <div style={{ width: 256, height: 256 }} className={style.cell} key="d" />
|
||||
// <div style={{ width: 256 * 2, height: 256 * 2 }} className={style.cell} key="h" />
|
||||
// <div style={{ width: 256 * 2, height: 256 }} className={style.cell} key="e" />
|
||||
// <div style={{ width: 256 * 2, height: 256 }} className={style.cell} key="f" />
|
||||
// <div style={{ width: 256 * 2, height: 256 }} className={style.cell} key="g" />
|
||||
// <div style={{ width: 256 * 2, height: 256 }} className={style.cell} key="g1" />
|
||||
// <div style={{ width: 256, height: 256 }} className={style.cell} key="d" />
|
||||
// <div style={{ width: 256, height: 256 }} className={style.cell} key="d1" />
|
||||
// <div style={{ width: 256, height: 256 }} className={style.cell} key="d2" />
|
||||
// <div style={{ width: 256, height: 256 }} className={style.cell} key="d3" />
|
||||
// <div style={{ width: 256, height: 256 }} className={style.cell} key="d4" />
|
||||
// </AutoResponsive>
|
||||
// );
|
||||
|
|
|
@ -14,8 +14,8 @@ $cols: $content_width / $cell;
|
|||
grid-auto-rows: 256px;
|
||||
grid-auto-flow: row dense;
|
||||
|
||||
grid-column-gap: 4px;
|
||||
grid-row-gap: 4px;
|
||||
grid-column-gap: $grid_line;
|
||||
grid-row-gap: $grid_line;
|
||||
}
|
||||
|
||||
.cell {
|
||||
|
@ -25,6 +25,7 @@ $cols: $content_width / $cell;
|
|||
flex: 0 0;
|
||||
background: $cell_bg;
|
||||
|
||||
@include outer_shadow();
|
||||
//&::after {
|
||||
// content: ' ';
|
||||
// background: transparentize(white, 0.9);
|
||||
|
|
20
src/components/input/Button/index.tsx
Normal file
20
src/components/input/Button/index.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import * as React from 'react';
|
||||
|
||||
const style = require('./style.scss');
|
||||
|
||||
interface IButtonProps {
|
||||
children?: string,
|
||||
label?: string,
|
||||
|
||||
onClick?: React.MouseEventHandler,
|
||||
}
|
||||
|
||||
export const Button: React.FunctionComponent<IButtonProps> = ({
|
||||
children,
|
||||
label,
|
||||
onClick = () => {},
|
||||
}) => (
|
||||
<div className={style.container} onClick={onClick}>
|
||||
{label || children || ''}
|
||||
</div>
|
||||
);
|
14
src/components/input/Button/style.scss
Normal file
14
src/components/input/Button/style.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
.container {
|
||||
height: $input_height;
|
||||
border-radius: $input_radius;
|
||||
display: flex;
|
||||
background: $button_bg_color;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
font-size: $text_small;
|
||||
cursor: pointer;
|
||||
|
||||
@include outer_shadow();
|
||||
}
|
21
src/components/input/Info/index.tsx
Normal file
21
src/components/input/Info/index.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import * as React from 'react';
|
||||
import classNames = require("classnames");
|
||||
|
||||
const style = require('./style.scss');
|
||||
|
||||
interface IInfoProps {
|
||||
text?: string,
|
||||
children?: string,
|
||||
level?: string,
|
||||
}
|
||||
export const Info: React.FunctionComponent<IInfoProps> = ({
|
||||
text,
|
||||
children,
|
||||
level = 'normal',
|
||||
}) => (
|
||||
<div className={classNames(style.container, { [level]: true })}>
|
||||
{
|
||||
text || children || ''
|
||||
}
|
||||
</div>
|
||||
);
|
28
src/components/input/Info/style.scss
Normal file
28
src/components/input/Info/style.scss
Normal file
|
@ -0,0 +1,28 @@
|
|||
.container {
|
||||
min-height: $info_height;
|
||||
border-radius: $input_radius;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: $text_small;
|
||||
line-height: 1.2em;
|
||||
padding: $gap;
|
||||
background: transparentize(white, 0.9);
|
||||
|
||||
&:global(.danger) {
|
||||
color: white;
|
||||
background: transparentize($color_red, 0.5);
|
||||
}
|
||||
|
||||
&:global(.warning) {
|
||||
color: white;
|
||||
background: transparentize($color_yellow, 0.5);
|
||||
}
|
||||
|
||||
&:global(.primary) {
|
||||
color: white;
|
||||
background: transparentize($color_blue, 0.5);
|
||||
}
|
||||
|
||||
}
|
||||
|
38
src/components/input/TextInput/index.tsx
Normal file
38
src/components/input/TextInput/index.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import * as React from 'react';
|
||||
|
||||
const style = require('./style.scss');
|
||||
|
||||
interface ITextInputProps {
|
||||
type?: 'text' | 'password',
|
||||
placeholder?: string,
|
||||
label?: string,
|
||||
value?: string,
|
||||
|
||||
onChange: React.ChangeEventHandler,
|
||||
}
|
||||
|
||||
export const TextInput: React.FunctionComponent<ITextInputProps> = ({
|
||||
type = 'text',
|
||||
placeholder = '',
|
||||
label,
|
||||
onChange = () => {},
|
||||
value='',
|
||||
}) => (
|
||||
<div
|
||||
className={style.wrapper}
|
||||
>
|
||||
{
|
||||
label &&
|
||||
<div className={style.label}>{label}</div>
|
||||
}
|
||||
<div className={style.container}>
|
||||
<input
|
||||
placeholder={placeholder}
|
||||
className={style.input}
|
||||
type={type}
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
37
src/components/input/TextInput/style.scss
Normal file
37
src/components/input/TextInput/style.scss
Normal file
|
@ -0,0 +1,37 @@
|
|||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.label {
|
||||
background: $input_bg_color;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
padding: 2px $gap;
|
||||
}
|
||||
|
||||
.container {
|
||||
height: $input_height;
|
||||
background: $input_bg_color;
|
||||
border-radius: $input_radius;
|
||||
flex-direction: row;
|
||||
flex: 1 0;
|
||||
display: flex;
|
||||
align-self: stretch;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include input_shadow();
|
||||
}
|
||||
|
||||
.input {
|
||||
outline: none;
|
||||
background: transparent;
|
||||
flex: 1;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
color: white;
|
||||
padding: 0 $gap;
|
||||
box-sizing: border-box;
|
||||
}
|
103
src/components/login/LoginForm/index.tsx
Normal file
103
src/components/login/LoginForm/index.tsx
Normal file
|
@ -0,0 +1,103 @@
|
|||
import * as React from 'react';
|
||||
import { TextInput } from "$components/input/TextInput";
|
||||
import { Button } from "$components/input/Button";
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from "redux";
|
||||
import { userSendLoginRequest, userSetLoginError } from "$redux/user/actions";
|
||||
import { IUserFormStateLogin, IUserState } from "$redux/user/reducer";
|
||||
import { Info } from "$components/input/Info";
|
||||
|
||||
const login = require('$containers/LoginLayout/style');
|
||||
const style = require('./style.scss');
|
||||
|
||||
interface ILoginFormProps {
|
||||
error: IUserFormStateLogin['error'],
|
||||
|
||||
userSendLoginRequest: typeof userSendLoginRequest,
|
||||
userSetLoginError: typeof userSetLoginError,
|
||||
}
|
||||
|
||||
interface ILoginFormState {
|
||||
username: string,
|
||||
password: string,
|
||||
}
|
||||
|
||||
class Component extends React.PureComponent<ILoginFormProps, ILoginFormState> {
|
||||
state = {
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
};
|
||||
|
||||
sendRequest = () => {
|
||||
console.log('send?');
|
||||
this.props.userSendLoginRequest(this.state);
|
||||
};
|
||||
|
||||
changeField = <T extends keyof ILoginFormState>(field: T) => ({ target: { value }}: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (this.props.error) this.props.userSetLoginError({ error: null });
|
||||
this.setState({ [field]: value } as Pick<ILoginFormState, keyof ILoginFormState>);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { error } = this.props;
|
||||
const { username, password } = this.state;
|
||||
|
||||
return (
|
||||
<div className={login.form}>
|
||||
<div className={style.container}>
|
||||
<div className={style.area_left}>
|
||||
|
||||
</div>
|
||||
<div className={style.area_right}>
|
||||
<div className={style.area_sign}>
|
||||
РЕШИТЕЛЬНО<br />ВОЙТИ
|
||||
</div>
|
||||
|
||||
<div className="spc double" />
|
||||
|
||||
<div className={style.inputs}>
|
||||
<TextInput
|
||||
label="Логин"
|
||||
value={username}
|
||||
onChange={this.changeField('username')}
|
||||
/>
|
||||
|
||||
<div className="gap" />
|
||||
|
||||
<TextInput
|
||||
label="Пароль"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={this.changeField('password')}
|
||||
/>
|
||||
|
||||
<div className="spc double" />
|
||||
|
||||
<Button onClick={this.sendRequest}>
|
||||
Войти
|
||||
</Button>
|
||||
|
||||
{
|
||||
error &&
|
||||
<React.Fragment>
|
||||
<div className="spc" />
|
||||
<Info>
|
||||
{error}
|
||||
</Info>
|
||||
</React.Fragment>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({ user: { form_state: { login }}}: { user: IUserState }) => ({ ...login });
|
||||
const mapDispatchToProps = dispatch => bindActionCreators({
|
||||
userSendLoginRequest,
|
||||
userSetLoginError,
|
||||
}, dispatch);
|
||||
|
||||
export const LoginForm = connect(mapStateToProps, mapDispatchToProps)(Component);
|
43
src/components/login/LoginForm/style.scss
Normal file
43
src/components/login/LoginForm/style.scss
Normal file
|
@ -0,0 +1,43 @@
|
|||
.container {
|
||||
display: grid;
|
||||
flex: 1;
|
||||
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
grid-row-gap: $grid_line;
|
||||
grid-column-gap: $grid_line;
|
||||
}
|
||||
|
||||
.area_left {
|
||||
grid-column-end: span 3;
|
||||
background: $content_bg_color;
|
||||
padding: $spc;
|
||||
border-radius: $panel_radius 0 0 $panel_radius;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@include outer_shadow();
|
||||
}
|
||||
|
||||
.area_right {
|
||||
grid-column-end: span 1;
|
||||
background: $content_bg_secondary;
|
||||
padding: $spc;
|
||||
border-radius: $panel_radius 0 0 $panel_radius;
|
||||
user-select: none;
|
||||
|
||||
@include outer_shadow();
|
||||
}
|
||||
|
||||
.area_sign {
|
||||
font-size: $text_sign;
|
||||
font-weight: 800;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.inputs {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
import * as React from 'react';
|
||||
import { Logo } from "$components/main/Logo";
|
||||
|
||||
const style = require('./style.scss');
|
||||
|
||||
export const Header = () => (
|
||||
<div className="default_container head_container">
|
||||
<div className={style.container}>
|
||||
<div className={style.logo}>
|
||||
VAULT
|
||||
</div>
|
||||
<Logo />
|
||||
<div className={style.spacer} />
|
||||
<div className={style.plugs}>
|
||||
<div>depth</div>
|
||||
|
|
|
@ -7,11 +7,6 @@
|
|||
height: 100px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 1.4em;
|
||||
font-weight: 800;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1;
|
||||
|
@ -36,7 +31,12 @@
|
|||
display: block;
|
||||
}
|
||||
|
||||
&:last-child::after { display: none; }
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
|
||||
&::after { display: none; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
8
src/components/main/Logo/index.tsx
Normal file
8
src/components/main/Logo/index.tsx
Normal file
|
@ -0,0 +1,8 @@
|
|||
import * as React from 'react';
|
||||
const style = require('./style.scss');
|
||||
|
||||
export const Logo = () => (
|
||||
<div className={style.logo}>
|
||||
VAULT
|
||||
</div>
|
||||
);
|
5
src/components/main/Logo/style.scss
Normal file
5
src/components/main/Logo/style.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
.logo {
|
||||
font-size: $text_sign;
|
||||
font-weight: 800;
|
||||
display: flex;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue