auth.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import styles from "./auth.module.scss";
  2. import { IconButton } from "./button";
  3. import { useState, useEffect } from "react";
  4. import { useNavigate } from "react-router-dom";
  5. import { Path, SAAS_CHAT_URL } from "../constant";
  6. import { useAccessStore } from "../store";
  7. import Locale from "../locales";
  8. import Delete from "../icons/www-delete.svg";
  9. import Arrow from "../icons/arrow.svg";
  10. import Logo from "../icons/logo.svg";
  11. import BotIcon from "../icons/bot.svg";
  12. import { getClientConfig } from "../config/client";
  13. import LeftIcon from "@/app/icons/left.svg";
  14. export function AuthPage() {
  15. const navigate = useNavigate();
  16. const accessStore = useAccessStore();
  17. const goHome = () => navigate(Path.Home);
  18. const goChat = () => navigate(Path.Chat);
  19. const goSaas = () => {
  20. window.location.href = SAAS_CHAT_URL;
  21. };
  22. const resetAccessCode = () => {
  23. accessStore.update((access) => {
  24. access.openaiApiKey = "";
  25. access.accessCode = "";
  26. });
  27. }; // Reset access code to empty string
  28. useEffect(() => {
  29. if (getClientConfig()?.isApp) {
  30. navigate(Path.Settings);
  31. }
  32. // eslint-disable-next-line react-hooks/exhaustive-deps
  33. }, []);
  34. return (
  35. <div className={styles["auth-page"]}>
  36. <TopBanner></TopBanner>
  37. <div className={styles["auth-header"]}>
  38. <IconButton
  39. icon={<LeftIcon />}
  40. text={Locale.Auth.Return}
  41. onClick={() => navigate(Path.Home)}
  42. ></IconButton>
  43. </div>
  44. <div className={`no-dark ${styles["auth-logo"]}`}>
  45. <BotIcon />
  46. </div>
  47. <div className={styles["auth-title"]}>{Locale.Auth.Title}</div>
  48. <div className={styles["auth-tips"]}>{Locale.Auth.Tips}</div>
  49. <input
  50. className={styles["auth-input"]}
  51. type="password"
  52. placeholder={Locale.Auth.Input}
  53. value={accessStore.accessCode}
  54. onChange={(e) => {
  55. accessStore.update(
  56. (access) => (access.accessCode = e.currentTarget.value),
  57. );
  58. }}
  59. />
  60. {!accessStore.hideUserApiKey ? (
  61. <>
  62. <div className={styles["auth-tips"]}>{Locale.Auth.SubTips}</div>
  63. <input
  64. className={styles["auth-input"]}
  65. type="password"
  66. placeholder={Locale.Settings.Access.OpenAI.ApiKey.Placeholder}
  67. value={accessStore.openaiApiKey}
  68. onChange={(e) => {
  69. accessStore.update(
  70. (access) => (access.openaiApiKey = e.currentTarget.value),
  71. );
  72. }}
  73. />
  74. <input
  75. className={styles["auth-input-second"]}
  76. type="password"
  77. placeholder={Locale.Settings.Access.Google.ApiKey.Placeholder}
  78. value={accessStore.googleApiKey}
  79. onChange={(e) => {
  80. accessStore.update(
  81. (access) => (access.googleApiKey = e.currentTarget.value),
  82. );
  83. }}
  84. />
  85. </>
  86. ) : null}
  87. <div className={styles["auth-actions"]}>
  88. <IconButton
  89. text={Locale.Auth.Confirm}
  90. type="primary"
  91. onClick={goChat}
  92. />
  93. <IconButton
  94. text={Locale.Auth.SaasTips}
  95. onClick={() => {
  96. goSaas();
  97. }}
  98. />
  99. </div>
  100. </div>
  101. );
  102. }
  103. function TopBanner() {
  104. const [isHovered, setIsHovered] = useState(false);
  105. const [isVisible, setIsVisible] = useState(true);
  106. useEffect(() => {
  107. // 检查 localStorage 中是否有标记
  108. const bannerDismissed = localStorage.getItem("bannerDismissed");
  109. // 如果标记不存在,存储默认值并显示横幅
  110. if (!bannerDismissed) {
  111. localStorage.setItem("bannerDismissed", "false");
  112. setIsVisible(true); // 显示横幅
  113. } else if (bannerDismissed === "true") {
  114. // 如果标记为 "true",则隐藏横幅
  115. setIsVisible(false);
  116. }
  117. }, []);
  118. const handleMouseEnter = () => {
  119. setIsHovered(true);
  120. };
  121. const handleMouseLeave = () => {
  122. setIsHovered(false);
  123. };
  124. const handleClose = () => {
  125. setIsVisible(false);
  126. localStorage.setItem("bannerDismissed", "true");
  127. };
  128. if (!isVisible) {
  129. return null;
  130. }
  131. return (
  132. <div
  133. className={styles["top-banner"]}
  134. onMouseEnter={handleMouseEnter}
  135. onMouseLeave={handleMouseLeave}
  136. >
  137. <div className={styles["top-banner-inner"]}>
  138. <span>
  139. <Logo></Logo>
  140. {Locale.Auth.TopTips}
  141. <a href={SAAS_CHAT_URL} rel="stylesheet">
  142. {Locale.Settings.Access.SaasStart.ChatNow}
  143. <Arrow style={{ marginLeft: "8px" }} />
  144. </a>
  145. </span>
  146. </div>
  147. {isHovered && (
  148. <Delete className={styles["top-banner-close"]} onClick={handleClose} />
  149. )}
  150. </div>
  151. );
  152. }