Skip to content

Commit

Permalink
feat(platform): add login route
Browse files Browse the repository at this point in the history
  • Loading branch information
xiejay97 committed Sep 15, 2022
1 parent 4c36143 commit 8a418f1
Show file tree
Hide file tree
Showing 15 changed files with 352 additions and 53 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,24 @@ jobs:
project: './dist/packages/site'
login: ${{ secrets.SURGE_LOGIN }}
token: ${{ secrets.SURGE_TOKEN }}
build-platform:
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: nrwl/nx-set-shas@v2
- uses: actions/setup-node@v2
with:
node-version: '16.13'
cache: 'yarn'
- run: yarn install --frozen-lockfile
- run: yarn nx build platform --configuration=production --skip-nx-cache
- run: cp ./dist/packages/platform/index.html ./dist/packages/platform/200.html
- uses: dswistowski/surge-sh-action@v1
with:
domain: 'rd-platform.surge.sh'
project: './dist/packages/platform'
login: ${{ secrets.SURGE_LOGIN }}
token: ${{ secrets.SURGE_TOKEN }}
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const fs = require('fs');

const message = process.env['HUSKY_GIT_PARAMS'];
const types = ['feat', 'fix', 'chore', 'docs', 'style', 'refactor', 'perf', 'test'];
const scopes = ['hooks', 'icons', 'site', 'ui', 'utils', 'vscode-extension'];
const scopes = ['hooks', 'icons', 'platform', 'site', 'ui', 'utils', 'vscode-extension'];

function parseMessage(message) {
const PATTERN = /^(\w*)(?:\((.*)\))?!?: (.*)$/;
Expand Down
1 change: 1 addition & 0 deletions packages/platform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"keywords": [
"react",
"devui",
"admin",
"platform"
],
"homepage": "https://github.com/DevCloudFE/react-devui/tree/main/packages/platform#readme",
Expand Down
196 changes: 171 additions & 25 deletions packages/platform/src/app/routes/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,191 @@
import type { UserState } from '../../config/state';

import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';

import { DButton } from '@react-devui/ui';
import { useAsync } from '@react-devui/hooks';
import { LockOutlined, MobileOutlined, UserOutlined } from '@react-devui/icons';
import { DButton, DCheckbox, DForm, DInput, DTabs, FormControl, FormGroup, useForm, Validators } from '@react-devui/ui';
import { getClassName } from '@react-devui/utils';

import { TOKEN } from '../../config/token';
import { useHttp, useInit, usePrevRoute } from '../hooks';
import { AppLanguage } from '../components';
import { useDeviceQuery, useHttp, useInit, usePrevRoute } from '../hooks';

export default function Login(): JSX.Element | null {
const { t } = useTranslation();
const createHttp = useHttp();
const [loading, setLoading] = useState(false);
const [loginloading, setLoginLoading] = useState(false);
const init = useInit();
const navigate = useNavigate();
const from = usePrevRoute();
const async = useAsync();
const deviceMatched = useDeviceQuery();

const [loginType, setLoginType] = useState<'account' | 'phone'>('account');
const [remember, setRemember] = useState(true);
const [codeDisabled, setCodeDisabled] = useState(0);
useEffect(() => {
if (codeDisabled > 0) {
async.setTimeout(() => {
setCodeDisabled((draft) => draft - 1);
}, 1000);
}
}, [async, codeDisabled]);

const accountForm = useForm(
() =>
new FormGroup({
username: new FormControl('', [
Validators.required,
(control) => {
return !control.value || control.value === 'admin' ? null : { checkValue: true };
},
]),
password: new FormControl('', Validators.required),
})
);
const phoneForm = useForm(
() =>
new FormGroup({
phone: new FormControl('', [Validators.required]),
code: new FormControl('', Validators.required),
})
);

const loginSameNode = (
<>
<DForm.Item>
<DCheckbox dModel={remember} onModelChange={setRemember}>
{t('routes.login.Remember me')}
</DCheckbox>
<a className="app-link" style={{ marginLeft: 'auto' }}>
{t('routes.login.Forgot Password')}
</a>
</DForm.Item>
<DForm.Item>
<DButton
type="submit"
disabled={loginType === 'account' ? accountForm.form.invalid : phoneForm.form.invalid}
onClick={() => {
const [http] = createHttp();
setLoginLoading(true);
http<{ user: UserState; token: string }>({
url: '/api/login',
method: 'post',
}).subscribe({
next: (res) => {
setLoginLoading(false);
TOKEN.token = res.token;
init(res.user);
navigate(from, { replace: true });
},
});
}}
dLoading={loginloading}
dBlock
>
{t('routes.login.Login')}
</DButton>
</DForm.Item>
</>
);

return (
<div>
Login
<DButton
onClick={() => {
const [http] = createHttp();
setLoading(true);
http<{ user: UserState; token: string }>({
url: '/api/login',
method: 'post',
}).subscribe({
next: (res) => {
setLoading(false);
TOKEN.token = res.token;
init(res.user);
navigate(from, { replace: true });
},
});
}}
dLoading={loading}
<div className="app-login-route">
<AppLanguage className="app-login-route__lang" />
<div>
{deviceMatched === 'desktop' && <img className="app-login-route__bg" src="/assets/login-bg.png" alt="bg" />}
<div className="app-login-route__login-container">
<div className="app-login-route__title-container">
<img className="app-login-route__logo" src="/assets/logo.svg" alt="Logo" />
<span>React DevUI</span>
</div>
<div className="app-login-route__description">{t('routes.login.description')}</div>
<DTabs
className="app-login-route__tabs"
dList={[
{
id: 'account',
title: t('routes.login.Account Login'),
panel: (
<DForm dForm={accountForm} dLabelWidth={0}>
<DForm.Item
dFormControls={{
username: {
required: t('routes.login.Please enter your name'),
checkValue: t('routes.login.Username'),
},
}}
>
{({ username }) => (
<DInput dFormControl={username} dPrefix={<UserOutlined />} dPlaceholder={t('routes.login.Username')} />
)}
</DForm.Item>
<DForm.Item dFormControls={{ password: t('routes.login.Please enter your password') }}>
{({ password }) => (
<DInput
dFormControl={password}
dPrefix={<LockOutlined />}
dPlaceholder={t('routes.login.Password')}
dType="password"
/>
)}
</DForm.Item>
{loginSameNode}
</DForm>
),
},
{
id: 'phone',
title: t('routes.login.Phone Login'),
panel: (
<DForm dForm={phoneForm} dLabelWidth={0}>
<DForm.Item dFormControls={{ phone: t('routes.login.Please enter your phone number') }}>
{({ phone }) => (
<DInput dFormControl={phone} dPrefix={<MobileOutlined />} dPlaceholder={t('routes.login.Phone number')} />
)}
</DForm.Item>
<DForm.Item dFormControls={{ code: t('routes.login.Please enter verification code') }} dSpan>
{({ code }) => <DInput dFormControl={code} dPlaceholder={t('routes.login.Verification code')} />}
</DForm.Item>
<DForm.Item dLabelWidth={8} dSpan="auto">
<DButton
disabled={codeDisabled > 0 || phoneForm.form.controls['phone'].invalid}
onClick={() => {
setCodeDisabled(60);
}}
>
{codeDisabled > 0 ? `${codeDisabled}s` : t('routes.login.Get code')}
</DButton>
</DForm.Item>
{loginSameNode}
</DForm>
),
},
]}
dActive={loginType}
onActiveChange={setLoginType}
/>
</div>
</div>
<footer
className={getClassName('app-login-route__footer', {
'app-login-route__footer--phone': deviceMatched === 'phone',
})}
>
{t('login.Login')}
</DButton>
<div>
<span>© 2022 made with ❤ by </span>
<a className="app-link" href="//github.com/xiejay97">
Xie Jay
</a>
</div>
<div className="app-login-route__link-container">
<a className="app-link">{t('routes.login.Terms')}</a>
<a className="app-link">{t('routes.login.Privacy')}</a>
</div>
</footer>
</div>
);
}
Binary file added packages/platform/src/assets/login-bg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 34 additions & 2 deletions packages/platform/src/i18n/resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,23 @@
"Logout": "Logout"
},
"login": {
"Login": "Login"
"Login": "Login",
"description": "Elegant Design & Ultimate Performance",
"Account Login": "Account Login",
"Phone Login": "Phone Login",
"Username": "Username: 'admin'",
"Password": "Password: any input",
"Please enter your name": "Please enter your name!",
"Please enter your password": "Please enter your password!",
"Remember me": "Remember me",
"Forgot Password": "Forgot Password?",
"Phone number": "Phone number",
"Verification code": "Verification code",
"Get code": "Get code",
"Please enter your phone number": "Please enter your phone number!",
"Please enter verification code": "Please enter verification code!",
"Terms": "Terms",
"Privacy": "Privacy"
}
}
},
Expand Down Expand Up @@ -58,7 +74,23 @@
"Logout": "退出登录"
},
"login": {
"Login": "登 录"
"Login": "登 录",
"description": "优雅的设计 & 极致的性能",
"Account Login": "账号密码登录",
"Phone Login": "手机号登录",
"Username": "用户名:'admin'",
"Password": "密码:任意输入",
"Please enter your name": "请输入用户名!",
"Please enter your password": "请输入密码!",
"Remember me": "记住我",
"Forgot Password": "忘记密码?",
"Phone number": "手机号",
"Verification code": "验证码",
"Get code": "获取验证码",
"Please enter your phone number": "请输入手机号!",
"Please enter verification code": "请输入验证码!",
"Terms": "条款",
"Privacy": "隐私"
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion packages/platform/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Platform</title>
<meta name="keywords" content="react,devui,platform" />
<meta name="keywords" content="react,devui,admin,platform" />
<meta name="description" content="An out-of-box solution for enterprise applications as a React boilerplate." />
<link rel="icon" type="image/svg+xml" href="/assets/logo.svg" />

Expand Down
23 changes: 19 additions & 4 deletions packages/platform/src/styles/_app.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@use 'sass:map';

*,
*::before,
*::after {
Expand All @@ -10,11 +12,24 @@ body {
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
font-weight: 400;
color: var(--d-text-color);
/* stylelint-disable-next-line declaration-property-value-allowed-list */
background-color: #fff;
background-color: map.get($colors, 'white');
text-size-adjust: 100%;
/* stylelint-disable-next-line declaration-property-value-allowed-list */
-webkit-tap-highlight-color: rgb(0 0 0 / 0%);

@include font-size(1rem);
}

.app-link {
font: inherit;
color: var(--#{$variable-prefix}color-primary);
text-decoration: none;
transition: color var(--#{$variable-prefix}animation-duration-fast) linear;

&:hover,
&:focus {
color: var(--#{$variable-prefix}color-primary-lighter);
}

&:active {
color: var(--#{$variable-prefix}color-primary-darker);
}
}
2 changes: 2 additions & 0 deletions packages/platform/src/styles/_routes.scss
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
@import 'routes/layout';

@import 'routes/login';
Loading

0 comments on commit 8a418f1

Please sign in to comment.