-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
196 additions
and
116 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import { Button, Checkbox, Input } from "@mui/joy"; | ||
import { ClientError } from "nice-grpc-web"; | ||
import { useEffect, useState } from "react"; | ||
import { toast } from "react-hot-toast"; | ||
import { authServiceClient } from "@/grpcweb"; | ||
import useLoading from "@/hooks/useLoading"; | ||
import useNavigateTo from "@/hooks/useNavigateTo"; | ||
import { useCommonContext } from "@/layouts/CommonContextProvider"; | ||
import { useUserStore } from "@/store/v1"; | ||
import { useTranslate } from "@/utils/i18n"; | ||
|
||
const PasswordSignInForm = () => { | ||
const t = useTranslate(); | ||
const navigateTo = useNavigateTo(); | ||
const commonContext = useCommonContext(); | ||
const userStore = useUserStore(); | ||
const actionBtnLoadingState = useLoading(false); | ||
const [username, setUsername] = useState(""); | ||
const [password, setPassword] = useState(""); | ||
const [remember, setRemember] = useState(true); | ||
|
||
useEffect(() => { | ||
if (commonContext.profile.mode === "demo") { | ||
setUsername("yourselfhosted"); | ||
setPassword("yourselfhosted"); | ||
} | ||
}, [commonContext.profile.mode]); | ||
|
||
const handleUsernameInputChanged = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
const text = e.target.value as string; | ||
setUsername(text); | ||
}; | ||
|
||
const handlePasswordInputChanged = (e: React.ChangeEvent<HTMLInputElement>) => { | ||
const text = e.target.value as string; | ||
setPassword(text); | ||
}; | ||
|
||
const handleFormSubmit = (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
handleSignInButtonClick(); | ||
}; | ||
|
||
const handleSignInButtonClick = async () => { | ||
if (username === "" || password === "") { | ||
return; | ||
} | ||
|
||
if (actionBtnLoadingState.isLoading) { | ||
return; | ||
} | ||
|
||
try { | ||
actionBtnLoadingState.setLoading(); | ||
await authServiceClient.signIn({ username, password, neverExpire: remember }); | ||
await userStore.fetchCurrentUser(); | ||
navigateTo("/"); | ||
} catch (error: any) { | ||
console.error(error); | ||
toast.error((error as ClientError).details || "Failed to sign in."); | ||
} | ||
actionBtnLoadingState.setFinish(); | ||
}; | ||
|
||
return ( | ||
<form className="w-full mt-2" onSubmit={handleFormSubmit}> | ||
<div className="flex flex-col justify-start items-start w-full gap-4"> | ||
<div className="w-full flex flex-col justify-start items-start"> | ||
<span className="leading-8 text-gray-600">{t("common.username")}</span> | ||
<Input | ||
className="w-full" | ||
size="lg" | ||
type="text" | ||
readOnly={actionBtnLoadingState.isLoading} | ||
placeholder={t("common.username")} | ||
value={username} | ||
autoComplete="username" | ||
autoCapitalize="off" | ||
spellCheck={false} | ||
onChange={handleUsernameInputChanged} | ||
required | ||
/> | ||
</div> | ||
<div className="w-full flex flex-col justify-start items-start"> | ||
<span className="leading-8 text-gray-600">{t("common.password")}</span> | ||
<Input | ||
className="w-full" | ||
size="lg" | ||
type="password" | ||
readOnly={actionBtnLoadingState.isLoading} | ||
placeholder={t("common.password")} | ||
value={password} | ||
autoComplete="password" | ||
autoCapitalize="off" | ||
spellCheck={false} | ||
onChange={handlePasswordInputChanged} | ||
required | ||
/> | ||
</div> | ||
</div> | ||
<div className="flex flex-row justify-start items-center w-full mt-6"> | ||
<Checkbox | ||
className="dark:!text-gray-400" | ||
label={t("common.remember-me")} | ||
checked={remember} | ||
onChange={(e) => setRemember(e.target.checked)} | ||
/> | ||
</div> | ||
<div className="flex flex-row justify-end items-center w-full mt-6"> | ||
<Button | ||
className="w-full" | ||
size="md" | ||
type="submit" | ||
disabled={actionBtnLoadingState.isLoading} | ||
loading={actionBtnLoadingState.isLoading} | ||
onClick={handleSignInButtonClick} | ||
> | ||
{t("common.sign-in")} | ||
</Button> | ||
</div> | ||
</form> | ||
); | ||
}; | ||
|
||
export default PasswordSignInForm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import AppearanceSelect from "@/components/AppearanceSelect"; | ||
import LocaleSelect from "@/components/LocaleSelect"; | ||
import PasswordSignInForm from "@/components/PasswordSignInForm"; | ||
import { useCommonContext } from "@/layouts/CommonContextProvider"; | ||
import { useWorkspaceSettingStore } from "@/store/v1"; | ||
import { WorkspaceGeneralSetting } from "@/types/proto/api/v1/workspace_setting_service"; | ||
import { WorkspaceSettingKey } from "@/types/proto/store/workspace_setting"; | ||
|
||
const AdminSignIn = () => { | ||
const commonContext = useCommonContext(); | ||
const workspaceSettingStore = useWorkspaceSettingStore(); | ||
const workspaceGeneralSetting = | ||
workspaceSettingStore.getWorkspaceSettingByKey(WorkspaceSettingKey.GENERAL).generalSetting || WorkspaceGeneralSetting.fromPartial({}); | ||
|
||
const handleLocaleSelectChange = (locale: Locale) => { | ||
commonContext.setLocale(locale); | ||
}; | ||
|
||
const handleAppearanceSelectChange = (appearance: Appearance) => { | ||
commonContext.setAppearance(appearance); | ||
}; | ||
|
||
return ( | ||
<div className="py-4 sm:py-8 w-80 max-w-full min-h-[100svh] mx-auto flex flex-col justify-start items-center"> | ||
<div className="w-full py-4 grow flex flex-col justify-center items-center"> | ||
<div className="w-full flex flex-row justify-center items-center mb-6"> | ||
<img className="h-14 w-auto rounded-full shadow" src={workspaceGeneralSetting.customProfile?.logoUrl || "/logo.webp"} alt="" /> | ||
<p className="ml-2 text-5xl text-black opacity-80 dark:text-gray-200"> | ||
{workspaceGeneralSetting.customProfile?.title || "Memos"} | ||
</p> | ||
</div> | ||
<p className="w-full text-xl font-medium">Sign in with admin accounts</p> | ||
<PasswordSignInForm /> | ||
</div> | ||
<div className="mt-4 flex flex-row items-center justify-center w-full gap-2"> | ||
<LocaleSelect value={commonContext.locale} onChange={handleLocaleSelectChange} /> | ||
<AppearanceSelect value={commonContext.appearance as Appearance} onChange={handleAppearanceSelectChange} /> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default AdminSignIn; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters