diff --git a/web/src/components/CreateTagDialog.tsx b/web/src/components/CreateTagDialog.tsx index cef9c543..7b4abdd1 100644 --- a/web/src/components/CreateTagDialog.tsx +++ b/web/src/components/CreateTagDialog.tsx @@ -1,4 +1,4 @@ -import { Input } from "@mui/joy"; +import { Button, Input } from "@mui/joy"; import React, { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { useTranslate } from "@/utils/i18n"; @@ -123,14 +123,14 @@ const CreateTagDialog: React.FC = (props: Props) => { {shownSuggestTagNameList.length > 0 && ( <>
- Tag suggestions - +
{showTagSuggestions && ( <> -
+
{shownSuggestTagNameList.map((tag) => ( = (props: Props) => { ))}
- + )} diff --git a/web/src/components/Header.tsx b/web/src/components/Header.tsx index 1553f3f0..56dd7686 100644 --- a/web/src/components/Header.tsx +++ b/web/src/components/Header.tsx @@ -1,7 +1,7 @@ import classNames from "classnames"; import { useEffect } from "react"; import { NavLink, useLocation } from "react-router-dom"; -import { useLayoutStore, useUserStore } from "@/store/module"; +import { useGlobalStore, useLayoutStore, useUserStore } from "@/store/module"; import { useTranslate } from "@/utils/i18n"; import { resolution } from "@/utils/layout"; import Icon from "./Icon"; @@ -12,6 +12,7 @@ import UpgradeVersionView from "./UpgradeVersionBanner"; const Header = () => { const t = useTranslate(); const location = useLocation(); + const globalStore = useGlobalStore(); const userStore = useUserStore(); const layoutStore = useLayoutStore(); const showHeader = layoutStore.state.showHeader; @@ -111,20 +112,22 @@ const Header = () => { {!isVisitorMode && ( <> - - classNames( - "px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700", - isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent" - ) - } - > - <> - {t("memo-chat.title")} - - + {globalStore.isDev() && ( + + classNames( + "px-4 pr-5 py-2 rounded-full border flex flex-row items-center text-lg text-gray-800 dark:text-gray-300 hover:bg-white hover:border-gray-200 dark:hover:border-zinc-600 dark:hover:bg-zinc-700", + isActive ? "bg-white dark:bg-zinc-700 border-gray-200 dark:border-zinc-600" : "border-transparent" + ) + } + > + <> + {t("memo-chat.title")} + + + )} { return (
{item.name}
{ closeConversation(e); }} diff --git a/web/src/components/MemoEditor/ResourceListView.tsx b/web/src/components/MemoEditor/ResourceListView.tsx index 48c07b40..e4009f5e 100644 --- a/web/src/components/MemoEditor/ResourceListView.tsx +++ b/web/src/components/MemoEditor/ResourceListView.tsx @@ -21,11 +21,11 @@ const ResourceListView = (props: Props) => { return (
- - {resource.filename} - handleDeleteResource(resource.id)} /> + + {resource.filename} + handleDeleteResource(resource.id)} />
); })} diff --git a/web/src/components/MemoEditor/index.tsx b/web/src/components/MemoEditor/index.tsx index 5e540314..74bfbb86 100644 --- a/web/src/components/MemoEditor/index.tsx +++ b/web/src/components/MemoEditor/index.tsx @@ -1,4 +1,3 @@ -import { Button } from "@mui/joy"; import { isNumber, last, uniq } from "lodash-es"; import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { toast } from "react-hot-toast"; @@ -422,9 +421,9 @@ const MemoEditor = (props: Props) => {
- +
diff --git a/web/src/components/MemoFilter.tsx b/web/src/components/MemoFilter.tsx index ed489597..50b60be4 100644 --- a/web/src/components/MemoFilter.tsx +++ b/web/src/components/MemoFilter.tsx @@ -31,6 +31,7 @@ const MemoFilter = () => { }} > {shortcut?.title} +
{ }} > {tagQuery} +
{ > {" "} {t(getTextWithMemoType(memoType as MemoSpecType) as Exclude, "">)} +
{ }} > {visibility} +
{duration && duration.from < duration.to ? (
{ to: getDateString(duration.to), interpolation: { escapeValue: false }, })} +
) : null}
{ }} > {textQuery} +
); diff --git a/web/src/components/ResourceIcon.tsx b/web/src/components/ResourceIcon.tsx index 01134dee..0b5d0b81 100644 --- a/web/src/components/ResourceIcon.tsx +++ b/web/src/components/ResourceIcon.tsx @@ -1,18 +1,33 @@ +import classNames from "classnames"; +import { getResourceUrl } from "@/utils/resource"; import Icon from "./Icon"; +import SquareDiv from "./kit/SquareDiv"; +import showPreviewImageDialog from "./PreviewImageDialog"; interface Props { className: string; - resourceType: string; + resource: Resource; } const ResourceIcon = (props: Props) => { - const { className, resourceType } = props; + const { className, resource } = props; - let ResourceIcon = Icon.FileText; - if (resourceType.includes("image")) { - ResourceIcon = Icon.Image; + if (resource.type.includes("image")) { + const url = getResourceUrl(resource); + return ( + + showPreviewImageDialog([url], 0)} + decoding="async" + loading="lazy" + /> + + ); } + const ResourceIcon = Icon.FileText; return ; }; diff --git a/web/src/components/Settings/MemberSection.tsx b/web/src/components/Settings/MemberSection.tsx index d873d46b..a00b63a2 100644 --- a/web/src/components/Settings/MemberSection.tsx +++ b/web/src/components/Settings/MemberSection.tsx @@ -1,4 +1,4 @@ -import { Table } from "@mui/joy"; +import { Button, Input } from "@mui/joy"; import React, { useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { useTranslate } from "@/utils/i18n"; @@ -7,7 +7,6 @@ import * as api from "@/helpers/api"; import Dropdown from "../kit/Dropdown"; import { showCommonDialog } from "../Dialog/CommonDialog"; import showChangeMemberPasswordDialog from "../ChangeMemberPasswordDialog"; -import "@/less/settings/member-section.less"; interface State { createUserUsername: string; @@ -30,7 +29,7 @@ const PreferencesSection = () => { const fetchUserList = async () => { const { data } = await api.getUserList(); - setUserList(data); + setUserList(data.sort((a, b) => a.id - b.id)); }; const handleUsernameInputChange = (event: React.ChangeEvent) => { @@ -117,94 +116,96 @@ const PreferencesSection = () => { return (

{t("setting.member-section.create-a-member")}

-
-
- {t("common.username")} - +
+
+ {t("common.username")} +
-
- {t("common.password")} - +
+ {t("common.password")} +
- +
-
+
{t("setting.member-list")}
- - - - - - - - - - {userList.map((user) => ( - - - - + + ))} + +
ID{t("common.username")}
{user.id}{user.username} - {currentUser?.id === user.id ? ( - {t("common.yourself")} - ) : ( - - - {user.rowStatus === "NORMAL" ? ( - - ) : ( +
+
+ + + + + + + + + + + + {userList.map((user) => ( + + + + + + - - ))} - -
+ ID + + {t("common.username")} + + {t("common.nickname")} + + {t("common.email")} +
{user.id}{user.username}{user.nickname}{user.email} + {currentUser?.id === user.id ? ( + {t("common.yourself")} + ) : ( + - + {user.rowStatus === "NORMAL" ? ( + + ) : ( + <> + + + + )} - )} - - } - /> - )} -
+ } + /> + )} +
+
+
); }; diff --git a/web/src/components/Settings/OpenAISection.tsx b/web/src/components/Settings/OpenAISection.tsx new file mode 100644 index 00000000..b8d32b8b --- /dev/null +++ b/web/src/components/Settings/OpenAISection.tsx @@ -0,0 +1,102 @@ +import { useEffect, useState } from "react"; +import { toast } from "react-hot-toast"; +import { useTranslate } from "@/utils/i18n"; +import { Button, Input } from "@mui/joy"; +import { useGlobalStore } from "@/store/module"; +import * as api from "@/helpers/api"; +import LearnMore from "../LearnMore"; +import "@/less/settings/system-section.less"; + +interface OpenAIConfig { + key: string; + host: string; +} + +const OpenAISection = () => { + const t = useTranslate(); + const globalStore = useGlobalStore(); + const [openAIConfig, setOpenAIConfig] = useState({ + key: "", + host: "", + }); + + useEffect(() => { + globalStore.fetchSystemStatus(); + }, []); + + useEffect(() => { + api.getSystemSetting().then(({ data: systemSettings }) => { + const openAIConfigSetting = systemSettings.find((setting) => setting.name === "openai-config"); + if (openAIConfigSetting) { + setOpenAIConfig(JSON.parse(openAIConfigSetting.value)); + } + }); + }, []); + + const handleOpenAIConfigKeyChanged = (value: string) => { + setOpenAIConfig({ + ...openAIConfig, + key: value, + }); + }; + + const handleOpenAIConfigHostChanged = (value: string) => { + setOpenAIConfig({ + ...openAIConfig, + host: value, + }); + }; + + const handleSaveOpenAIConfig = async () => { + try { + await api.upsertSystemSetting({ + name: "openai-config", + value: JSON.stringify(openAIConfig), + }); + } catch (error) { + console.error(error); + return; + } + toast.success("OpenAI Config updated"); + }; + + return ( +
+

{t("setting.openai")}

+
+
+ {t("setting.system-section.openai-api-key")} + +
+
+ handleOpenAIConfigKeyChanged(event.target.value)} + /> +
+ {t("setting.system-section.openai-api-host")} +
+ handleOpenAIConfigHostChanged(event.target.value)} + /> +
+ +
+
+ ); +}; + +export default OpenAISection; diff --git a/web/src/components/UpdateCustomizedProfileDialog.tsx b/web/src/components/UpdateCustomizedProfileDialog.tsx index 799e4721..07e73123 100644 --- a/web/src/components/UpdateCustomizedProfileDialog.tsx +++ b/web/src/components/UpdateCustomizedProfileDialog.tsx @@ -1,3 +1,4 @@ +import { Button, Input } from "@mui/joy"; import { useState } from "react"; import { useTranslate } from "@/utils/i18n"; import { toast } from "react-hot-toast"; @@ -103,9 +104,9 @@ const UpdateCustomizedProfileDialog: React.FC = ({ destroy }: Props) => { {t("setting.system-section.server-name")} ({t("setting.system-section.customize-server.default")})

- +

{t("setting.system-section.customize-server.icon-url")}

- +

{t("setting.system-section.customize-server.description")}