Browse Source

登录 修改 注册 本地化接口已写

main
lk 2 months ago
parent
commit
28cd4e28ed
  1. 91
      src/pages/use/login/index.tsx
  2. 86
      src/pages/use/password/retrieve.tsx
  3. 68
      src/pages/use/register/index.tsx

91
src/pages/use/login/index.tsx

@ -1,22 +1,38 @@
import { Layout, Tabs, Input, Button, Typography, Row, Col, Form } from "antd"; import { Layout, Tabs, Input, Button, Typography, Row, Col, Form } from "antd";
import { QrcodeOutlined, UserOutlined, LockOutlined } from "@ant-design/icons";
import SelectLang from "@/components/select-lang";
import { UserOutlined, LockOutlined } from "@ant-design/icons";
import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { useAtom, useAtomValue } from "jotai";
import { useTranslation } from "@/i18n.ts";
import {
emailCodeAtom,
emailLoginAtom,
telegramCodeAtom,
telegramLoginAtom,
upLoginAtom,
} from "@/store/system/user.ts";
import { useAtomValue } from "jotai";
import { emailLoginAtom, telegramLoginAtom, upLoginAtom } from "@/store/system/user.ts";
import React, { memo, useEffect, useLayoutEffect, useState } from "react"; import React, { memo, useEffect, useLayoutEffect, useState } from "react";
import systemServ from "@/service/system.ts"; import systemServ from "@/service/system.ts";
import { t } from "i18next";
const { Title, Text, Link } = Typography; const { Title, Text, Link } = Typography;
const { TabPane } = Tabs; const { TabPane } = Tabs;
const i18nPrefix = "login";
const Login = memo(() => { const Login = memo(() => {
const languageSet = {
邮箱密码登录: t(`${i18nPrefix}.emailPasswordLogin`, "邮箱密码登录"),
向量检索服务免费试用: t(`${i18nPrefix}.vectorRetrievalServiceFreeTrial`, "向量检索服务免费试用"),
免费试用向量检索服务玩转大模型生成式检索: t(
`${i18nPrefix}.freeTrialVectorRetrievalService`,
"免费试用向量检索服务,玩转大模型生成式检索",
),
查看详情: t(`${i18nPrefix}.viewDetails`, "查看详情 >"),
请输入邮箱: t(`${i18nPrefix}.pleaseEnterEmail`, "请输入邮箱"),
请输入登录密码: t(`${i18nPrefix}.pleaseEnterPassword`, "请输入登录密码"),
登录: t(`${i18nPrefix}.login`, "登录"),
邮箱验证登录: t(`${i18nPrefix}.emailVerificationLogin`, "邮箱验证登录"),
获得验证码: t(`${i18nPrefix}.getVerificationCode`, "获得验证码"),
请输入验证码: t(`${i18nPrefix}.pleaseEnterVerificationCode`, "请输入验证码"),
飞机验证登录: t(`${i18nPrefix}.telegramVerificationLogin`, "飞机验证登录"),
获得飞机验证码: t(`${i18nPrefix}.getTelegramVerificationCode`, "获得飞机验证码"),
请输入飞机验证码: t(`${i18nPrefix}.pleaseEnterTelegramVerificationCode`, "请输入飞机验证码"),
注册: t(`${i18nPrefix}.register`, "注册"),
忘记密码: t(`${i18nPrefix}.forgotPassword`, "忘记密码"),
};
const navigate = useNavigate(); const navigate = useNavigate();
const [upform] = Form.useForm(); const [upform] = Form.useForm();
const [emailform] = Form.useForm(); const [emailform] = Form.useForm();
@ -100,10 +116,10 @@ const Login = memo(() => {
<Col> <Col>
<Row> <Row>
<Col span={12} style={{ padding: "20px" }}> <Col span={12} style={{ padding: "20px" }}>
<Title level={3}></Title>
<Text></Text>
<Title level={3}>{languageSet.}</Title>
<Text>{languageSet.}</Text>
<br /> <br />
<Link href="#"> &gt;</Link>
<Link href="#">{languageSet.}</Link>
</Col> </Col>
<Col <Col
span={12} span={12}
@ -117,78 +133,75 @@ const Login = memo(() => {
}} }}
> >
<Tabs defaultActiveKey="1" style={{ marginTop: "50px" }}> <Tabs defaultActiveKey="1" style={{ marginTop: "50px" }}>
<TabPane tab="邮箱密码登录" key="1">
<TabPane tab={languageSet.} key="1">
<Form form={upform} onFinish={uphandleSubmit}> <Form form={upform} onFinish={uphandleSubmit}>
<Form.Item name="username" rules={[{ required: true, message: "请输入邮箱" }]}>
<Input size="large" placeholder="请输入邮箱" prefix={<UserOutlined />} />
<Form.Item name="username" rules={[{ required: true, message: languageSet.请输入邮箱 }]}>
<Input size="large" placeholder={languageSet.} prefix={<UserOutlined />} />
</Form.Item> </Form.Item>
<Form.Item name="password" rules={[{ required: true, message: "请输入登录密码" }]}>
<Form.Item name="password" rules={[{ required: true, message: languageSet.请输入登录密码 }]}>
<Input.Password <Input.Password
size="large" size="large"
placeholder="请输入登录密码"
placeholder={languageSet.}
prefix={<LockOutlined />} prefix={<LockOutlined />}
autoComplete="off" autoComplete="off"
/> />
</Form.Item> </Form.Item>
{/*<Form.Item name="code" rules={[{ required: true, message: "请输入验证码" }]}>*/}
{/* <Input.Password size="large" placeholder="验证码" prefix={<LockOutlined />} />*/}
{/*</Form.Item>*/}
<Button type="primary" htmlType="submit" style={{ width: "100%" }}> <Button type="primary" htmlType="submit" style={{ width: "100%" }}>
{languageSet.}
</Button> </Button>
</Form> </Form>
</TabPane> </TabPane>
<TabPane tab="邮箱验证登录" key="2">
<TabPane tab={languageSet.} key="2">
<Form form={emailform} onFinish={emailhandleSubmit}> <Form form={emailform} onFinish={emailhandleSubmit}>
<Form.Item name="email" rules={[{ required: true, message: "请输入邮箱" }]}>
<Form.Item name="email" rules={[{ required: true, message: languageSet.请输入邮箱 }]}>
<Input <Input
size="large" size="large"
placeholder="请输入邮箱"
placeholder={languageSet.}
prefix={<UserOutlined />} prefix={<UserOutlined />}
addonAfter={ addonAfter={
<Button onClick={getEmailCode} disabled={isButtonDisabled}> <Button onClick={getEmailCode} disabled={isButtonDisabled}>
{isButtonDisabled ? `${countdown}秒后重试` : "获得验证码"}
{isButtonDisabled ? `${countdown}秒后重试` : languageSet.}
</Button> </Button>
} }
/> />
</Form.Item> </Form.Item>
<Form.Item name="code" rules={[{ required: true, message: "请输入验证码" }]}>
<Form.Item name="code" rules={[{ required: true, message: languageSet.请输入验证码 }]}>
<Input.Password <Input.Password
size="large" size="large"
placeholder="请输入验证码"
placeholder={languageSet.}
prefix={<LockOutlined />} prefix={<LockOutlined />}
autoComplete="off" autoComplete="off"
/> />
</Form.Item> </Form.Item>
<Button type="primary" htmlType="submit" style={{ width: "100%" }}> <Button type="primary" htmlType="submit" style={{ width: "100%" }}>
{languageSet.}
</Button> </Button>
</Form> </Form>
</TabPane> </TabPane>
<TabPane tab="飞机验证登录" key="3">
<TabPane tab={languageSet.} key="3">
<Form form={telegramform} onFinish={telegramhandleSubmit}> <Form form={telegramform} onFinish={telegramhandleSubmit}>
<Form.Item name="telegram" rules={[{ required: true, message: "请输入邮箱" }]}>
<Form.Item name="telegram" rules={[{ required: true, message: languageSet.请输入邮箱 }]}>
<Input <Input
size="large" size="large"
placeholder="请输入邮箱"
placeholder={languageSet.}
prefix={<UserOutlined />} prefix={<UserOutlined />}
addonAfter={ addonAfter={
<Button onClick={getTelegramCode} disabled={isButtonDisabled}> <Button onClick={getTelegramCode} disabled={isButtonDisabled}>
{isButtonDisabled ? `${countdown}秒后重试` : "获得飞机验证码"}
{isButtonDisabled ? `${countdown}秒后重试` : languageSet.}
</Button> </Button>
} }
/> />
</Form.Item> </Form.Item>
<Form.Item name="code" rules={[{ required: true, message: "请输入飞机验证码" }]}>
<Form.Item name="code" rules={[{ required: true, message: languageSet.请输入飞机验证码 }]}>
<Input.Password <Input.Password
size="large" size="large"
placeholder="请输入飞机验证码"
placeholder={languageSet.}
prefix={<LockOutlined />} prefix={<LockOutlined />}
autoComplete="off" autoComplete="off"
/> />
</Form.Item> </Form.Item>
<Button type="primary" htmlType="submit" style={{ width: "100%" }}> <Button type="primary" htmlType="submit" style={{ width: "100%" }}>
{languageSet.}
</Button> </Button>
</Form> </Form>
</TabPane> </TabPane>
@ -220,11 +233,11 @@ const Login = memo(() => {
}} }}
onClick={() => navigate({ to: "/register" })} onClick={() => navigate({ to: "/register" })}
> >
{languageSet.}
</span> </span>
</div> </div>
<Row justify="space-between" style={{ marginTop: "10px" }}> <Row justify="space-between" style={{ marginTop: "10px" }}>
<Link onClick={() => navigate({ to: "/pwdRetrieve" })}></Link>
<Link onClick={() => navigate({ to: "/pwdRetrieve" })}>{languageSet.}</Link>
</Row> </Row>
</Col> </Col>
</Row> </Row>

86
src/pages/use/password/retrieve.tsx

@ -1,13 +1,32 @@
import {Layout, Input, Button, Typography, Row, Col, Form, message, Modal} from "antd";
import { Layout, Input, Button, Typography, Row, Col, Form, message, Modal } from "antd";
import { LockOutlined, MailOutlined, SecurityScanOutlined } from "@ant-design/icons"; import { LockOutlined, MailOutlined, SecurityScanOutlined } from "@ant-design/icons";
import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { useAtomValue } from "jotai";
import React, { memo, useEffect, useState } from "react"; import React, { memo, useEffect, useState } from "react";
import { emailCodeAtom } from "@/store/system/user.ts";
import { passwordRules } from "@/pages/use/useTools.tsx"; import { passwordRules } from "@/pages/use/useTools.tsx";
import { useAtom } from "jotai/index";
import systemServ from "@/service/system.ts"; import systemServ from "@/service/system.ts";
import { setToken } from "@/store/system.ts";
import { t } from "i18next";
const languageSet = {
向量检索服务免费试用: t("pwdRetrieve.vectorRetrievalServiceFreeTrial", "向量检索服务免费试用"),
免费试用向量检索服务玩转大模型生成式检索: t(
"pwdRetrieve.freeTrialVectorRetrievalService",
"免费试用向量检索服务,玩转大模型生成式检索",
),
查看详情: t("pwdRetrieve.viewDetails", "查看详情 &gt;"),
请输入邮箱: t("pwdRetrieve.pleaseEnterEmail", "请输入邮箱"),
获得验证码: t("pwdRetrieve.getVerificationCode", "获得验证码"),
秒后重试: t("pwdRetrieve.retryAfterSeconds", "秒后重试"),
请输入验证码: t("pwdRetrieve.pleaseEnterVerificationCode", "请输入验证码"),
请输入新密码: t("pwdRetrieve.pleaseEnterNewPassword", "请输入新密码"),
请确认密码: t("pwdRetrieve.confirmPassword", "请确认密码"),
两次输入的密码不一致: t("pwdRetrieve.passwordsDoNotMatch", "两次输入的密码不一致"),
找回密码: t("pwdRetrieve.retrievePassword", "找回密码"),
返回登录: t("pwdRetrieve.backToLogin", "返回登录"),
修改成功: t("pwdRetrieve.changeSuccess", "修改成功"),
确认: t("pwdRetrieve.confirm", "确认"),
密码修改成功返回登录: t("pwdRetrieve.passwordChangeSuccess", "密码修改成功,返回登录。"),
};
const { Title, Text, Link } = Typography; const { Title, Text, Link } = Typography;
const PwdRetrieve = memo(() => { const PwdRetrieve = memo(() => {
@ -34,12 +53,9 @@ const PwdRetrieve = memo(() => {
const [countdown, setCountdown] = useState<number>(0); const [countdown, setCountdown] = useState<number>(0);
const [isButtonDisabled, setIsButtonDisabled] = useState<boolean>(false); const [isButtonDisabled, setIsButtonDisabled] = useState<boolean>(false);
const [gotoLoginModalVisible, SetGotoLoginModalVisible] = useState(false); const [gotoLoginModalVisible, SetGotoLoginModalVisible] = useState(false);
const showGotoLoginModal= () => {
const showGotoLoginModal = () => {
SetGotoLoginModalVisible(true); SetGotoLoginModalVisible(true);
}; };
@ -48,12 +64,6 @@ const PwdRetrieve = memo(() => {
navigate({ to: "/login" }); navigate({ to: "/login" });
}; };
// const gotoLoginModalHandleCancel = () => {
// SetGotoLoginModalVisible(false);
// };
//
useEffect(() => { useEffect(() => {
if ((emailCodeData as any)?.code === 0) { if ((emailCodeData as any)?.code === 0) {
setCountdown(10); setCountdown(10);
@ -63,7 +73,7 @@ const PwdRetrieve = memo(() => {
useEffect(() => { useEffect(() => {
if ((pwdRetrieveData as any)?.code === 0) { if ((pwdRetrieveData as any)?.code === 0) {
message.success("密码找回成功");
message.success(languageSet.);
showGotoLoginModal(); showGotoLoginModal();
} }
}, [pwdRetrieveData, navigate]); }, [pwdRetrieveData, navigate]);
@ -97,10 +107,10 @@ const PwdRetrieve = memo(() => {
<Col> <Col>
<Row> <Row>
<Col span={12} style={{ padding: "20px" }}> <Col span={12} style={{ padding: "20px" }}>
<Title level={3}></Title>
<Text></Text>
<Title level={3}>{languageSet.}</Title>
<Text>{languageSet.}</Text>
<br /> <br />
<Link href="#"> &gt;</Link>
<Link href="#">{languageSet.}</Link>
</Col> </Col>
<Col <Col
span={12} span={12}
@ -114,25 +124,30 @@ const PwdRetrieve = memo(() => {
}} }}
> >
<Form form={retrieveForm} onFinish={handleRetrieveSubmit} style={{ marginTop: "50px" }}> <Form form={retrieveForm} onFinish={handleRetrieveSubmit} style={{ marginTop: "50px" }}>
<Form.Item name="email" rules={[{ required: true, message: "请输入邮箱" }]}>
<Form.Item name="email" rules={[{ required: true, message: languageSet.请输入邮箱 }]}>
<Input <Input
size="large" size="large"
placeholder="请输入邮箱"
placeholder={languageSet.}
prefix={<MailOutlined />} prefix={<MailOutlined />}
addonAfter={ addonAfter={
<Button onClick={handleGetCode} disabled={isButtonDisabled}> <Button onClick={handleGetCode} disabled={isButtonDisabled}>
{isButtonDisabled ? `${countdown}秒后重试` : "获得验证码"}
{isButtonDisabled ? `${countdown}${languageSet.}` : languageSet.}
</Button> </Button>
} }
/> />
</Form.Item> </Form.Item>
<Form.Item name="code" rules={[{ required: true, message: "请输入验证码" }]}>
<Input size="large" placeholder="请输入验证码" prefix={<SecurityScanOutlined />} autoComplete="off" />
<Form.Item name="code" rules={[{ required: true, message: languageSet.请输入验证码 }]}>
<Input
size="large"
placeholder={languageSet.}
prefix={<SecurityScanOutlined />}
autoComplete="off"
/>
</Form.Item> </Form.Item>
<Form.Item name="password" rules={passwordRules}> <Form.Item name="password" rules={passwordRules}>
<Input.Password <Input.Password
size="large" size="large"
placeholder="请输入新密码"
placeholder={languageSet.}
prefix={<LockOutlined />} prefix={<LockOutlined />}
autoComplete="off" autoComplete="off"
/> />
@ -141,41 +156,46 @@ const PwdRetrieve = memo(() => {
name="confirmPassword" name="confirmPassword"
dependencies={["password"]} dependencies={["password"]}
rules={[ rules={[
{ required: true, message: "请确认密码" },
{ required: true, message: languageSet.请确认密码 },
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(_, value) { validator(_, value) {
if (!value || getFieldValue("password") === value) { if (!value || getFieldValue("password") === value) {
return Promise.resolve(); return Promise.resolve();
} }
return Promise.reject(new Error("两次输入的密码不一致"));
return Promise.reject(new Error(languageSet.));
}, },
}), }),
]} ]}
> >
<Input.Password size="large" placeholder="请确认密码" prefix={<LockOutlined />} autoComplete="off" />
<Input.Password
size="large"
placeholder={languageSet.}
prefix={<LockOutlined />}
autoComplete="off"
/>
</Form.Item> </Form.Item>
<Button type="primary" htmlType="submit" style={{ width: "100%" }}> <Button type="primary" htmlType="submit" style={{ width: "100%" }}>
{languageSet.}
</Button> </Button>
</Form> </Form>
<Row justify="space-between" style={{ marginTop: "10px" }}> <Row justify="space-between" style={{ marginTop: "10px" }}>
<Link onClick={() => navigate({ to: "/login" })}></Link>
<Link onClick={() => navigate({ to: "/login" })}>{languageSet.}</Link>
</Row> </Row>
</Col> </Col>
</Row> </Row>
</Col> </Col>
</Row> </Row>
<Modal <Modal
title="修改成功"
title={languageSet.}
visible={gotoLoginModalVisible} visible={gotoLoginModalVisible}
onOk={gotoLoginModalHandleOk} onOk={gotoLoginModalHandleOk}
footer={[ footer={[
<Button key="ok" type="primary" onClick={gotoLoginModalHandleOk}> <Button key="ok" type="primary" onClick={gotoLoginModalHandleOk}>
{languageSet.}
</Button>, </Button>,
]} ]}
> >
<p></p>
<p>{languageSet.}</p>
</Modal> </Modal>
</Layout> </Layout>
); );

68
src/pages/use/register/index.tsx

@ -1,11 +1,32 @@
import { Layout, Input, Button, Typography, Row, Col, Form } from "antd"; import { Layout, Input, Button, Typography, Row, Col, Form } from "antd";
import {LockOutlined, MailOutlined, SecurityScanOutlined, UserOutlined} from "@ant-design/icons";
import { LockOutlined, MailOutlined, SecurityScanOutlined } from "@ant-design/icons";
import { createFileRoute, useNavigate } from "@tanstack/react-router"; import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import React, { memo, useEffect, useState } from "react"; import React, { memo, useEffect, useState } from "react";
import { emailRegisterAtom } from "@/store/system/user.ts"; import { emailRegisterAtom } from "@/store/system/user.ts";
import { passwordRules } from "@/pages/use/useTools"; import { passwordRules } from "@/pages/use/useTools";
import systemServ from "@/service/system.ts"; import systemServ from "@/service/system.ts";
import { t } from "i18next";
const languageSet = {
向量检索服务免费试用: t("register.vectorRetrievalServiceFreeTrial", "向量检索服务免费试用"),
免费试用向量检索服务玩转大模型生成式检索: t(
"register.freeTrialVectorRetrievalService",
"免费试用向量检索服务,玩转大模型生成式检索",
),
查看详情: t("register.viewDetails", "查看详情 &gt;"),
请输入邮箱: t("register.pleaseEnterEmail", "请输入邮箱"),
获得验证码: t("register.getVerificationCode", "获得验证码"),
秒后重试: t("register.retryAfterSeconds", "秒后重试"),
请输入验证码: t("register.pleaseEnterVerificationCode", "请输入验证码"),
请输入密码: t("register.pleaseEnterPassword", "请输入密码"),
请确认密码: t("register.confirmPassword", "请确认密码"),
两次输入的密码不一致: t("register.passwordsDoNotMatch", "两次输入的密码不一致"),
注册: t("register.register", "注册"),
登录: t("register.login", "登录"),
返回登录: t("register.backToLogin", "返回登录"),
};
const { Title, Text, Link } = Typography; const { Title, Text, Link } = Typography;
const Register = memo(() => { const Register = memo(() => {
@ -62,10 +83,10 @@ const Register = memo(() => {
<Col> <Col>
<Row> <Row>
<Col span={12} style={{ padding: "20px" }}> <Col span={12} style={{ padding: "20px" }}>
<Title level={3}></Title>
<Text></Text>
<Title level={3}>{languageSet.}</Title>
<Text>{languageSet.}</Text>
<br /> <br />
<Link href="#"> &gt;</Link>
<Link href="#">{languageSet.}</Link>
</Col> </Col>
<Col <Col
span={12} span={12}
@ -79,43 +100,58 @@ const Register = memo(() => {
}} }}
> >
<Form form={registerForm} onFinish={emailRegisterSubmit} style={{ marginTop: "50px" }}> <Form form={registerForm} onFinish={emailRegisterSubmit} style={{ marginTop: "50px" }}>
<Form.Item name="email" rules={[{ required: true, message: "请输入邮箱" }]}>
<Form.Item name="email" rules={[{ required: true, message: languageSet.请输入邮箱 }]}>
<Input <Input
size="large" size="large"
placeholder="请输入邮箱"
placeholder={languageSet.}
prefix={<MailOutlined />} prefix={<MailOutlined />}
addonAfter={ addonAfter={
<Button onClick={getEmailCode} disabled={isButtonDisabled}> <Button onClick={getEmailCode} disabled={isButtonDisabled}>
{isButtonDisabled ? `${countdown}秒后重试` : "获得验证码"}
{isButtonDisabled ? `${countdown}${languageSet.}` : languageSet.}
</Button> </Button>
} }
/> />
</Form.Item> </Form.Item>
<Form.Item name="code" rules={[{ required: true, message: "请输入验证码" }]}>
<Input size="large" placeholder="请输入验证码" prefix={<SecurityScanOutlined />} autoComplete="off" />
<Form.Item name="code" rules={[{ required: true, message: languageSet.请输入验证码 }]}>
<Input
size="large"
placeholder={languageSet.}
prefix={<SecurityScanOutlined />}
autoComplete="off"
/>
</Form.Item> </Form.Item>
<Form.Item name="password" rules={passwordRules}> <Form.Item name="password" rules={passwordRules}>
<Input.Password size="large" placeholder="请输入密码" prefix={<LockOutlined />} autoComplete="off" />
<Input.Password
size="large"
placeholder={languageSet.}
prefix={<LockOutlined />}
autoComplete="off"
/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="confirmPassword" name="confirmPassword"
dependencies={["password"]} dependencies={["password"]}
rules={[ rules={[
{ required: true, message: "请确认密码" },
{ required: true, message: languageSet.请确认密码 },
({ getFieldValue }) => ({ ({ getFieldValue }) => ({
validator(_, value) { validator(_, value) {
if (!value || getFieldValue("password") === value) { if (!value || getFieldValue("password") === value) {
return Promise.resolve(); return Promise.resolve();
} }
return Promise.reject(new Error("两次输入的密码不一致"));
return Promise.reject(new Error(languageSet.));
}, },
}), }),
]} ]}
> >
<Input.Password size="large" placeholder="请确认密码" prefix={<LockOutlined />} autoComplete="off" />
<Input.Password
size="large"
placeholder={languageSet.}
prefix={<LockOutlined />}
autoComplete="off"
/>
</Form.Item> </Form.Item>
<Button type="primary" htmlType="submit" style={{ width: "100%" }}> <Button type="primary" htmlType="submit" style={{ width: "100%" }}>
{languageSet.}
</Button> </Button>
</Form> </Form>
<div <div
@ -145,11 +181,11 @@ const Register = memo(() => {
}} }}
onClick={() => navigate({ to: "/login" })} onClick={() => navigate({ to: "/login" })}
> >
{languageSet.}
</span> </span>
</div> </div>
<Row justify="space-between" style={{ marginTop: "10px" }}> <Row justify="space-between" style={{ marginTop: "10px" }}>
<Link onClick={() => navigate({ to: "/login" })}></Link>
<Link onClick={() => navigate({ to: "/login" })}>{languageSet.}</Link>
</Row> </Row>
</Col> </Col>
</Row> </Row>

Loading…
Cancel
Save