diff --git a/packages/ui/package.json b/packages/ui/package.json index 1d077f6a9..3b0ef968e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -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", diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index 69e972957..89e4b84d7 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -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 = () => ( - + + ); diff --git a/packages/ui/src/apis/consent.ts b/packages/ui/src/apis/consent.ts new file mode 100644 index 000000000..8445d2b57 --- /dev/null +++ b/packages/ui/src/apis/consent.ts @@ -0,0 +1,8 @@ +import ky from 'ky'; + +export const consent = async () => { + type Response = { + redirectTo: string; + }; + return ky.post('/api/sign-in/consent').json(); +}; diff --git a/packages/ui/src/apis/sign-in.ts b/packages/ui/src/apis/sign-in.ts new file mode 100644 index 000000000..97120ca44 --- /dev/null +++ b/packages/ui/src/apis/sign-in.ts @@ -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(); +}; diff --git a/packages/ui/src/locales/en.json b/packages/ui/src/locales/en.json index 800079b63..0bb8ab813 100644 --- a/packages/ui/src/locales/en.json +++ b/packages/ui/src/locales/en.json @@ -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" } } diff --git a/packages/ui/src/locales/zh-CN.json b/packages/ui/src/locales/zh-CN.json index caa320b9e..f241078b9 100644 --- a/packages/ui/src/locales/zh-CN.json +++ b/packages/ui/src/locales/zh-CN.json @@ -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": "密码" } } diff --git a/packages/ui/src/pages/Consent/index.tsx b/packages/ui/src/pages/Consent/index.tsx new file mode 100644 index 000000000..d18ac2e49 --- /dev/null +++ b/packages/ui/src/pages/Consent/index.tsx @@ -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
{t('sign_in.loading')}
; +}; + +export default Consent; diff --git a/packages/ui/src/pages/Home/index.module.scss b/packages/ui/src/pages/SignIn/index.module.scss similarity index 92% rename from packages/ui/src/pages/Home/index.module.scss rename to packages/ui/src/pages/SignIn/index.module.scss index b3accd735..1c75520c7 100644 --- a/packages/ui/src/pages/Home/index.module.scss +++ b/packages/ui/src/pages/SignIn/index.module.scss @@ -20,8 +20,9 @@ } > input:not([type='button']) { - align-self: stretch; margin-top: _.unit(3); + width: 100%; + max-width: 320px; } > input[type='button'] { diff --git a/packages/ui/src/pages/Home/index.tsx b/packages/ui/src/pages/SignIn/index.tsx similarity index 62% rename from packages/ui/src/pages/Home/index.tsx rename to packages/ui/src/pages/SignIn/index.tsx index 2af88ea1d..c4f901ed0 100644 --- a/packages/ui/src/pages/Home/index.tsx +++ b/packages/ui/src/pages/SignIn/index.tsx @@ -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('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 (
登录 Logto
{pageState === 'error' && ( - {t('sign-in.error')} + {t('sign_in.error')} )}