mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(sign-in): impl. api calls and redirects
This commit is contained in:
parent
0dd5b8808d
commit
6d030a4ad3
10 changed files with 83 additions and 22 deletions
|
@ -5,7 +5,7 @@
|
|||
"scripts": {
|
||||
"dev:tsc": "tsc -b -w --preserveWatchOutput",
|
||||
"dev:razzle": "razzle start",
|
||||
"start": "concurrently -c \"blue,cyan\" \"yarn:dev:tsc\" -k \"yarn:dev:razzle\"",
|
||||
"start": "PORT=5000 concurrently -c \"blue,cyan\" \"yarn:dev:tsc\" -k \"yarn:dev:razzle\"",
|
||||
"start:prod": "NODE_ENV=production node build/server.js",
|
||||
"build": "tsc -b && razzle build",
|
||||
"lint": "eslint --format pretty --ext .ts --ext .tsx src",
|
||||
|
@ -17,6 +17,7 @@
|
|||
"classnames": "^2.3.1",
|
||||
"i18next": "^20.3.3",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"ky": "^0.28.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-i18next": "^11.11.3",
|
||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import { Route, Switch } from 'react-router-dom';
|
||||
import AppContent from './components/AppContent';
|
||||
import initI18n from './init/i18n';
|
||||
import Home from './pages/Home';
|
||||
import Consent from './pages/Consent';
|
||||
import SignIn from './pages/SignIn';
|
||||
import './scss/normalized.scss';
|
||||
|
||||
initI18n();
|
||||
|
@ -10,7 +11,8 @@ initI18n();
|
|||
const App = () => (
|
||||
<AppContent theme="dark">
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route exact path="/sign-in" component={SignIn} />
|
||||
<Route exact path="/sign-in/consent" component={Consent} />
|
||||
</Switch>
|
||||
</AppContent>
|
||||
);
|
||||
|
|
8
packages/ui/src/apis/consent.ts
Normal file
8
packages/ui/src/apis/consent.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import ky from 'ky';
|
||||
|
||||
export const consent = async () => {
|
||||
type Response = {
|
||||
redirectTo: string;
|
||||
};
|
||||
return ky.post('/api/sign-in/consent').json<Response>();
|
||||
};
|
15
packages/ui/src/apis/sign-in.ts
Normal file
15
packages/ui/src/apis/sign-in.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import ky from 'ky';
|
||||
|
||||
export const signInBasic = async (username: string, password: string) => {
|
||||
type Response = {
|
||||
redirectTo: string;
|
||||
};
|
||||
return ky
|
||||
.post('/api/sign-in', {
|
||||
json: {
|
||||
id: username,
|
||||
password,
|
||||
},
|
||||
})
|
||||
.json<Response>();
|
||||
};
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"translation": {
|
||||
"sign-in": "Sign In",
|
||||
"sign-in.loading": "Signing in...",
|
||||
"sign-in.error": "Username or password invalid.",
|
||||
"sign-in.username": "Username",
|
||||
"sign-in.password": "Password"
|
||||
"sign_in": "Sign In",
|
||||
"sign_in.loading": "Signing in...",
|
||||
"sign_in.error": "Username or password invalid.",
|
||||
"sign_in.username": "Username",
|
||||
"sign_in.password": "Password"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"translation": {
|
||||
"sign-in": "登录",
|
||||
"sign-in.loading": "登录中...",
|
||||
"sign-in.error": "用户名或密码错误。",
|
||||
"sign-in.username": "用户名",
|
||||
"sign-in.password": "密码"
|
||||
"sign_in": "登录",
|
||||
"sign_in.loading": "登录中...",
|
||||
"sign_in.error": "用户名或密码错误。",
|
||||
"sign_in.username": "用户名",
|
||||
"sign_in.password": "密码"
|
||||
}
|
||||
}
|
||||
|
|
19
packages/ui/src/pages/Consent/index.tsx
Normal file
19
packages/ui/src/pages/Consent/index.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { consent } from '@/apis/consent';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const Consent = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const autoConsent = async () => {
|
||||
window.location.href = (await consent()).redirectTo;
|
||||
};
|
||||
|
||||
void autoConsent();
|
||||
}, []);
|
||||
|
||||
return <div>{t('sign_in.loading')}</div>;
|
||||
};
|
||||
|
||||
export default Consent;
|
|
@ -20,8 +20,9 @@
|
|||
}
|
||||
|
||||
> input:not([type='button']) {
|
||||
align-self: stretch;
|
||||
margin-top: _.unit(3);
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
}
|
||||
|
||||
> input[type='button'] {
|
|
@ -1,7 +1,8 @@
|
|||
import { signInBasic } from '@/apis/sign-in';
|
||||
import Button from '@/components/Button';
|
||||
import Input from '@/components/Input';
|
||||
import MessageBox from '@/components/MessageBox';
|
||||
import React, { useState } from 'react';
|
||||
import React, { FormEventHandler, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styles from './index.module.scss';
|
||||
|
||||
|
@ -14,33 +15,42 @@ const Home = () => {
|
|||
const [pageState, setPageState] = useState<PageState>('idle');
|
||||
const isLoading = pageState === 'loading';
|
||||
|
||||
const signIn: FormEventHandler = async (event) => {
|
||||
event.preventDefault();
|
||||
setPageState('loading');
|
||||
try {
|
||||
window.location.href = (await signInBasic(username, password)).redirectTo;
|
||||
} catch {
|
||||
// TODO: Show specific error after merge into monorepo
|
||||
setPageState('error');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form className={styles.wrapper}>
|
||||
<div className={styles.title}>登录 Logto</div>
|
||||
<Input
|
||||
autoComplete="username"
|
||||
isDisabled={isLoading}
|
||||
placeholder={t('sign-in.username')}
|
||||
placeholder={t('sign_in.username')}
|
||||
value={username}
|
||||
onChange={setUsername}
|
||||
/>
|
||||
<Input
|
||||
autoComplete="current-password"
|
||||
isDisabled={isLoading}
|
||||
placeholder={t('sign-in.password')}
|
||||
placeholder={t('sign_in.password')}
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={setPassword}
|
||||
/>
|
||||
{pageState === 'error' && (
|
||||
<MessageBox className={styles.box}>{t('sign-in.error')}</MessageBox>
|
||||
<MessageBox className={styles.box}>{t('sign_in.error')}</MessageBox>
|
||||
)}
|
||||
<Button
|
||||
isDisabled={isLoading}
|
||||
value={isLoading ? t('sign-in.loading') : t('sign-in')}
|
||||
onClick={() => {
|
||||
setPageState('loading');
|
||||
}}
|
||||
value={isLoading ? t('sign_in.loading') : t('sign_in')}
|
||||
onClick={signIn}
|
||||
/>
|
||||
</form>
|
||||
);
|
|
@ -7108,6 +7108,11 @@ known-css-properties@^0.21.0:
|
|||
resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.21.0.tgz#15fbd0bbb83447f3ce09d8af247ed47c68ede80d"
|
||||
integrity sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==
|
||||
|
||||
ky@^0.28.5:
|
||||
version "0.28.5"
|
||||
resolved "https://registry.yarnpkg.com/ky/-/ky-0.28.5.tgz#4b7ada24fb0440c3898406f3a4986abe60ba213e"
|
||||
integrity sha512-O5gg9kF4MeyfSw+YkgPAafOPwEUU6xcdGEJKUJmKpIPbLzk3oxUtY4OdBNekG7mawofzkyZ/ZHuR9ev5uZZdAA==
|
||||
|
||||
leven@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
|
||||
|
|
Loading…
Reference in a new issue