diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 91ed9861..398df569 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -747,6 +747,13 @@ ui: lang: label: Interface Language text: User interface language. It will change when you refresh the page. + my_logins: + title: My Logins + lable: Log in or sign up on this site using these accounts. + modal_title: Remove Login + modal_content: Are you sure you want to remove this login from your account? + modal_confirm_btn: Remove + remove_success: Removed successfully toast: update: update success update_password: Password changed successfully. @@ -1362,7 +1369,7 @@ ui: btn_submit: Save not_found_props: "Required property {{ key }} not found." select: Select - + page_review: review: Review proposed: proposed diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index af9653f7..6c04c748 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -521,3 +521,14 @@ export interface OauthBindEmailReq { email: string; must: boolean; } + +export interface OauthConnectorItem { + icon: string; + name: string; + link: string; +} + +export interface UserOauthConnectorItem extends OauthConnectorItem { + binding: boolean; + external_id: string; +} diff --git a/ui/src/components/OauthButtons/index.tsx b/ui/src/components/OauthButtons/index.tsx deleted file mode 100644 index 27611cca..00000000 --- a/ui/src/components/OauthButtons/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { memo, FC } from 'react'; -import { Button } from 'react-bootstrap'; - -import classnames from 'classnames'; - -import { Icon } from '@/components'; - -interface Props { - // data: any[]; // should use oauth plugin schemes - className?: string; -} -const Index: FC = ({ className }) => { - return ( -
- - - -
- ); -}; - -export default memo(Index); diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index e1c4254f..2a475e8b 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -32,7 +32,6 @@ import CustomizeTheme from './CustomizeTheme'; import PageTags from './PageTags'; import QuestionListLoader from './QuestionListLoader'; import TagsLoader from './TagsLoader'; -import OauthButtons from './OauthButtons'; export { Avatar, @@ -71,6 +70,5 @@ export { PageTags, QuestionListLoader, TagsLoader, - OauthButtons, }; export type { EditorRef, JSONSchema, UISchema }; diff --git a/ui/src/index.scss b/ui/src/index.scss index 6619a3fa..9c34473b 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -302,3 +302,9 @@ a { .bg-fade-out { animation: 2s ease 1s bg-fade-out; } + + +.btnSvg, .btnSvg:hover { + fill: currentColor; + vertical-align: -0.125em; +} diff --git a/ui/src/pages/Users/Login/index.tsx b/ui/src/pages/Users/Login/index.tsx index 5c2b6683..afde7d34 100644 --- a/ui/src/pages/Users/Login/index.tsx +++ b/ui/src/pages/Users/Login/index.tsx @@ -11,7 +11,8 @@ import type { ImgCodeRes, FormDataType, } from '@/common/interface'; -import { Unactivate, OauthButtons } from '@/components'; +import { Unactivate } from '@/components'; +import { PluginOauth } from '@/plugins'; import { loggedUserInfoStore, loginSettingStore } from '@/stores'; import { guard, floppyNavigation, handleFormError } from '@/utils'; import { login, checkImgCode } from '@/services'; @@ -173,7 +174,7 @@ const Index: React.FC = () => {

{t('page_title')}

{step === 1 && ( - +
{t('email.label')} diff --git a/ui/src/pages/Users/Settings/Account/components/MyLogins/index.tsx b/ui/src/pages/Users/Settings/Account/components/MyLogins/index.tsx index 0b72211b..8c568cdb 100644 --- a/ui/src/pages/Users/Settings/Account/components/MyLogins/index.tsx +++ b/ui/src/pages/Users/Settings/Account/components/MyLogins/index.tsx @@ -1,40 +1,65 @@ import { memo } from 'react'; import { Button } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; -import { Icon, Modal } from '@/components'; +import { Modal } from '@/components'; +import { useOauthConnectorInfoByUser, userOauthUnbind } from '@/services'; +import { useToast } from '@/hooks'; const Index = () => { - const deleteLogins = (type) => { + const { data, mutate } = useOauthConnectorInfoByUser(); + const toast = useToast(); + + const { t } = useTranslation('translation', { + keyPrefix: 'settings.my_logins', + }); + + const deleteLogins = (e, item) => { + if (!item.binding) { + return; + } + e.preventDefault(); Modal.confirm({ - title: 'Remove Login', - content: 'Are you sure you want to delete this logins?', + title: t('modal_title'), + content: t('modal_content'), confirmBtnVariant: 'danger', - confirmText: 'Remove', + confirmText: t('modal_confirm_btn'), onConfirm: () => { - console.log('delete login by: ', type); + userOauthUnbind({ external_id: item.external_id }).then(() => { + mutate(); + toast.onShow({ + msg: t('remove_success'), + variant: 'success', + }); + }); }, }); }; return (
-
My Logins
- - Log in or sign up on this site using these accounts. - +
{t('title')}
+ {t('lable')}
- - - + {data?.map((item) => { + return ( + + ); + })}
); diff --git a/ui/src/plugins/PluginOauth/index.tsx b/ui/src/plugins/PluginOauth/index.tsx new file mode 100644 index 00000000..b61997db --- /dev/null +++ b/ui/src/plugins/PluginOauth/index.tsx @@ -0,0 +1,36 @@ +import { memo, FC } from 'react'; +import { Button } from 'react-bootstrap'; + +import classnames from 'classnames'; + +import { useGetStartUseOauthConnector } from '@/services'; + +interface Props { + className?: string; +} +const Index: FC = ({ className }) => { + const { data } = useGetStartUseOauthConnector(); + + if (!data?.length) return null; + return ( +
+ {data?.map((item) => { + return ( + + ); + })} +
+ ); +}; + +export default memo(Index); diff --git a/ui/src/plugins/index.ts b/ui/src/plugins/index.ts new file mode 100644 index 00000000..b38e66fd --- /dev/null +++ b/ui/src/plugins/index.ts @@ -0,0 +1,3 @@ +import PluginOauth from './PluginOauth'; + +export { PluginOauth }; diff --git a/ui/src/services/client/Oauth.ts b/ui/src/services/client/Oauth.ts index 8a679663..b02e88c8 100644 --- a/ui/src/services/client/Oauth.ts +++ b/ui/src/services/client/Oauth.ts @@ -1,6 +1,36 @@ +import useSWR from 'swr'; + import request from '@/utils/request'; import type * as Type from '@/common/interface'; export const oAuthBindEmail = (data: Type.OauthBindEmailReq) => { return request.post('/answer/api/v1/connector/binding/email', data); }; + +export const useOauthConnectorInfoByUser = () => { + const { data, error, mutate } = useSWR( + '/answer/api/v1/connector/user/info', + request.instance.get, + ); + return { + data, + mutate, + isLoading: !data && !error, + error, + }; +}; + +export const userOauthUnbind = (data: { external_id: string }) => { + return request.delete('/answer/api/v1/connector/user/unbinding', data); +}; + +export const useGetStartUseOauthConnector = () => { + const { data, error } = useSWR( + '/answer/api/v1/connector/info', + request.instance.get, + ); + return { + data, + error, + }; +};