Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
220cba84
Unverified
Commit
220cba84
authored
Jul 16, 2023
by
boojack
Committed by
GitHub
Jul 16, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: add dev guard for memo chat (#1968)
parent
032509dd
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
284 additions
and
163 deletions
+284
-163
CreateTagDialog.tsx
web/src/components/CreateTagDialog.tsx
+7
-7
Header.tsx
web/src/components/Header.tsx
+18
-15
ConversationTab.tsx
web/src/components/MemoChat/ConversationTab.tsx
+2
-2
ResourceListView.tsx
web/src/components/MemoEditor/ResourceListView.tsx
+4
-4
index.tsx
web/src/components/MemoEditor/index.tsx
+2
-3
MemoFilter.tsx
web/src/components/MemoFilter.tsx
+6
-0
ResourceIcon.tsx
web/src/components/ResourceIcon.tsx
+20
-5
MemberSection.tsx
web/src/components/Settings/MemberSection.tsx
+80
-79
OpenAISection.tsx
web/src/components/Settings/OpenAISection.tsx
+102
-0
UpdateCustomizedProfileDialog.tsx
web/src/components/UpdateCustomizedProfileDialog.tsx
+8
-9
tailwind.css
web/src/css/tailwind.css
+1
-1
member-section.less
web/src/less/settings/member-section.less
+0
-21
en.json
web/src/locales/en.json
+1
-0
Auth.tsx
web/src/pages/Auth.tsx
+2
-2
Explore.tsx
web/src/pages/Explore.tsx
+4
-1
MemoChat.tsx
web/src/pages/MemoChat.tsx
+8
-11
Setting.tsx
web/src/pages/Setting.tsx
+19
-3
No files found.
web/src/components/CreateTagDialog.tsx
View file @
220cba84
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: Props) => {
{
shownSuggestTagNameList
.
length
>
0
&&
(
<>
<
div
className=
"mt-4 mb-1 text-sm w-full flex flex-row justify-start items-center"
>
<
span
className=
"text-gray-400"
>
Tag suggestions
</
span
>
<
button
className=
"btn-normal ml-2 px-2 py-0 leading-6 font-mono
"
onClick=
{
handleToggleShowSuggestionTags
}
>
<
span
className=
"text-gray-400
mr-2
"
>
Tag suggestions
</
span
>
<
Button
size=
"sm"
variant=
"outlined
"
onClick=
{
handleToggleShowSuggestionTags
}
>
{
showTagSuggestions
?
"hide"
:
"show"
}
</
b
utton
>
</
B
utton
>
</
div
>
{
showTagSuggestions
&&
(
<>
<
div
className=
"w-full flex flex-row justify-start items-start flex-wrap"
>
<
div
className=
"w-full flex flex-row justify-start items-start flex-wrap
mb-2
"
>
{
shownSuggestTagNameList
.
map
((
tag
)
=>
(
<
span
className=
"max-w-[120px] text-sm mr-2 mt-1 font-mono cursor-pointer truncate dark:text-gray-300 hover:opacity-60"
...
...
@@ -141,9 +141,9 @@ const CreateTagDialog: React.FC<Props> = (props: Props) => {
</
span
>
))
}
</
div
>
<
button
className=
"btn-normal mt-2 px-2 py-0 leading-6 font-mono
"
onClick=
{
handleSaveSuggestTagList
}
>
<
Button
size=
"sm
"
onClick=
{
handleSaveSuggestTagList
}
>
Save all
</
b
utton
>
</
B
utton
>
</>
)
}
</>
...
...
web/src/components/Header.tsx
View file @
220cba84
import
classNames
from
"classnames"
;
import
{
useEffect
}
from
"react"
;
import
{
NavLink
,
useLocation
}
from
"react-router-dom"
;
import
{
useLayoutStore
,
useUserStore
}
from
"@/store/module"
;
import
{
use
GlobalStore
,
use
LayoutStore
,
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
&&
(
<>
<
NavLink
to=
"/memo-chat"
id=
"header-memo-chat"
className=
{
({
isActive
})
=>
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"
)
}
>
<>
<
Icon
.
Bot
className=
"mr-3 w-6 h-auto opacity-70"
/>
{
t
(
"memo-chat.title"
)
}
</>
</
NavLink
>
{
globalStore
.
isDev
()
&&
(
<
NavLink
to=
"/memo-chat"
id=
"header-memo-chat"
className=
{
({
isActive
})
=>
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"
)
}
>
<>
<
Icon
.
Bot
className=
"mr-3 w-6 h-auto opacity-70"
/>
{
t
(
"memo-chat.title"
)
}
</>
</
NavLink
>
)
}
<
NavLink
to=
"/archived"
id=
"header-archived"
...
...
web/src/components/MemoChat/ConversationTab.tsx
View file @
220cba84
...
...
@@ -11,7 +11,7 @@ interface ConversationTabProps {
const
ConversationTab
=
({
item
,
selectedConversationId
,
setSelectedConversationId
,
closeConversation
}:
ConversationTabProps
)
=>
{
return
(
<
div
className=
{
`flex rounded-lg h-8 px-3 border dark:border-zinc-600 ${
className=
{
`flex rounded-lg h-8 px-3
cursor-pointer
border dark:border-zinc-600 ${
selectedConversationId === item.messageStorageId ? "bg-white dark:bg-zinc-700" : "bg-gray-200 dark:bg-zinc-800 opacity-60"
}`
}
key=
{
item
.
messageStorageId
}
...
...
@@ -21,7 +21,7 @@ const ConversationTab = ({ item, selectedConversationId, setSelectedConversation
>
<
div
className=
"truncate m-auto"
>
{
item
.
name
}
</
div
>
<
Icon
.
X
className=
"
w-4 h-auto m-auto cursor-pointer
"
className=
"
ml-1 w-4 h-auto m-auto cursor-pointer opacity-60
"
onClick=
{
(
e
:
any
)
=>
{
closeConversation
(
e
);
}
}
...
...
web/src/components/MemoEditor/ResourceListView.tsx
View file @
220cba84
...
...
@@ -21,11 +21,11 @@ const ResourceListView = (props: Props) => {
return
(
<
div
key=
{
resource
.
id
}
className=
"max-w-full flex flex-row justify-start items-center flex-nowrap bg-gray-100 dark:bg-zinc-800
hover:opacity-80 px-2 py-1 rounded cursor-pointer
text-gray-500"
className=
"max-w-full flex flex-row justify-start items-center flex-nowrap bg-gray-100 dark:bg-zinc-800
px-2 py-1 rounded
text-gray-500"
>
<
ResourceIcon
resource
Type=
{
resource
.
typ
e
}
className=
"w-4 h-auto mr-1"
/>
<
span
className=
"text-sm max-w-xs truncate
font-mono
"
>
{
resource
.
filename
}
</
span
>
<
Icon
.
X
className=
"w-4 h-auto ml-1 hover:opacity-80"
onClick=
{
()
=>
handleDeleteResource
(
resource
.
id
)
}
/>
<
ResourceIcon
resource
=
{
resourc
e
}
className=
"w-4 h-auto mr-1"
/>
<
span
className=
"text-sm max-w-xs truncate"
>
{
resource
.
filename
}
</
span
>
<
Icon
.
X
className=
"w-4 h-auto ml-1
cursor-pointer
hover:opacity-80"
onClick=
{
()
=>
handleDeleteResource
(
resource
.
id
)
}
/>
</
div
>
);
})
}
...
...
web/src/components/MemoEditor/index.tsx
View file @
220cba84
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) => {
<
div
className=
"editor-footer-container"
>
<
MemoVisibilitySelector
value=
{
state
.
memoVisibility
}
onChange=
{
handleMemoVisibilityChange
}
/>
<
div
className=
"buttons-container"
>
<
Button
disabled=
{
!
allowSave
}
onClick=
{
handleSaveBtnClick
}
>
<
button
className=
"action-btn confirm-btn"
disabled=
{
!
allowSave
}
onClick=
{
handleSaveBtnClick
}
>
{
t
(
"editor.save"
)
}
</
B
utton
>
</
b
utton
>
</
div
>
</
div
>
</
div
>
...
...
web/src/components/MemoFilter.tsx
View file @
220cba84
...
...
@@ -31,6 +31,7 @@ const MemoFilter = () => {
}
}
>
<
Icon
.
Target
className=
"icon-text"
/>
{
shortcut
?.
title
}
<
Icon
.
X
className=
"w-4 h-auto ml-1 opacity-40"
/>
</
div
>
<
div
className=
{
"filter-item-container "
+
(
tagQuery
?
""
:
"!hidden"
)
}
...
...
@@ -39,6 +40,7 @@ const MemoFilter = () => {
}
}
>
<
Icon
.
Tag
className=
"icon-text"
/>
{
tagQuery
}
<
Icon
.
X
className=
"w-4 h-auto ml-1 opacity-40"
/>
</
div
>
<
div
className=
{
"filter-item-container "
+
(
memoType
?
""
:
"!hidden"
)
}
...
...
@@ -48,6 +50,7 @@ const MemoFilter = () => {
>
<
Icon
.
Box
className=
"icon-text"
/>
{
" "
}
{
t
(
getTextWithMemoType
(
memoType
as
MemoSpecType
)
as
Exclude
<
ReturnType
<
typeof
getTextWithMemoType
>
,
""
>
)
}
<
Icon
.
X
className=
"w-4 h-auto ml-1 opacity-40"
/>
</
div
>
<
div
className=
{
"filter-item-container "
+
(
visibility
?
""
:
"!hidden"
)
}
...
...
@@ -56,6 +59,7 @@ const MemoFilter = () => {
}
}
>
<
Icon
.
Eye
className=
"icon-text"
/>
{
visibility
}
<
Icon
.
X
className=
"w-4 h-auto ml-1 opacity-40"
/>
</
div
>
{
duration
&&
duration
.
from
<
duration
.
to
?
(
<
div
...
...
@@ -70,6 +74,7 @@ const MemoFilter = () => {
to
:
getDateString
(
duration
.
to
),
interpolation
:
{
escapeValue
:
false
},
})
}
<
Icon
.
X
className=
"w-4 h-auto ml-1 opacity-40"
/>
</
div
>
)
:
null
}
<
div
...
...
@@ -79,6 +84,7 @@ const MemoFilter = () => {
}
}
>
<
Icon
.
Search
className=
"icon-text"
/>
{
textQuery
}
<
Icon
.
X
className=
"w-4 h-auto ml-1 opacity-40"
/>
</
div
>
</
div
>
);
...
...
web/src/components/ResourceIcon.tsx
View file @
220cba84
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
;
resource
Type
:
string
;
resource
:
Resource
;
}
const
ResourceIcon
=
(
props
:
Props
)
=>
{
const
{
className
,
resource
Type
}
=
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
(
<
SquareDiv
key=
{
resource
.
id
}
className=
{
classNames
(
"cursor-pointer rounded hover:shadow"
,
className
)
}
>
<
img
className=
"min-h-full min-w-full w-auto h-auto rounded"
src=
{
resource
.
externalLink
?
url
:
url
+
"?thumbnail=1"
}
onClick=
{
()
=>
showPreviewImageDialog
([
url
],
0
)
}
decoding=
"async"
loading=
"lazy"
/>
</
SquareDiv
>
);
}
const
ResourceIcon
=
Icon
.
FileText
;
return
<
ResourceIcon
className=
{
className
}
/>;
};
...
...
web/src/components/Settings/MemberSection.tsx
View file @
220cba84
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
<
HTMLInputElement
>
)
=>
{
...
...
@@ -117,94 +116,96 @@ const PreferencesSection = () => {
return
(
<
div
className=
"section-container member-section-container"
>
<
p
className=
"title-text"
>
{
t
(
"setting.member-section.create-a-member"
)
}
</
p
>
<
div
className=
"create-member-container"
>
<
div
className=
"input-form-container"
>
<
span
className=
"field-text"
>
{
t
(
"common.username"
)
}
</
span
>
<
input
type=
"text"
autoComplete=
"new-password"
placeholder=
{
t
(
"common.username"
)
}
value=
{
state
.
createUserUsername
}
onChange=
{
handleUsernameInputChange
}
/>
<
div
className=
"w-full flex flex-col justify-start items-start gap-2"
>
<
div
className=
"flex flex-col justify-start items-start gap-1"
>
<
span
className=
"text-sm"
>
{
t
(
"common.username"
)
}
</
span
>
<
Input
type=
"text"
placeholder=
{
t
(
"common.username"
)
}
value=
{
state
.
createUserUsername
}
onChange=
{
handleUsernameInputChange
}
/>
</
div
>
<
div
className=
"input-form-container"
>
<
span
className=
"field-text"
>
{
t
(
"common.password"
)
}
</
span
>
<
input
type=
"password"
autoComplete=
"new-password"
placeholder=
{
t
(
"common.password"
)
}
value=
{
state
.
createUserPassword
}
onChange=
{
handlePasswordInputChange
}
/>
<
div
className=
"flex flex-col justify-start items-start gap-1"
>
<
span
className=
"text-sm"
>
{
t
(
"common.password"
)
}
</
span
>
<
Input
type=
"password"
placeholder=
{
t
(
"common.password"
)
}
value=
{
state
.
createUserPassword
}
onChange=
{
handlePasswordInputChange
}
/>
</
div
>
<
div
className=
"btns-container"
>
<
button
className=
"btn-normal"
onClick=
{
handleCreateUserBtnClick
}
>
{
t
(
"common.create"
)
}
</
button
>
<
Button
onClick=
{
handleCreateUserBtnClick
}
>
{
t
(
"common.create"
)
}
</
Button
>
</
div
>
</
div
>
<
div
className=
"w-full flex flex-row justify-between items-center"
>
<
div
className=
"w-full flex flex-row justify-between items-center
mt-6
"
>
<
div
className=
"title-text"
>
{
t
(
"setting.member-list"
)
}
</
div
>
</
div
>
<
Table
>
<
thead
>
<
tr
>
<
th
>
ID
</
th
>
<
th
>
{
t
(
"common.username"
)
}
</
th
>
<
th
></
th
>
</
tr
>
</
thead
>
<
tbody
>
{
userList
.
map
((
user
)
=>
(
<
tr
key=
{
user
.
id
}
>
<
td
className=
"field-text id-text"
>
{
user
.
id
}
</
td
>
<
td
className=
"field-text username-text"
>
{
user
.
username
}
</
td
>
<
td
className=
"flex flex-row justify-end items-center"
>
{
currentUser
?.
id
===
user
.
id
?
(
<
span
className=
"tip-text"
>
{
t
(
"common.yourself"
)
}
</
span
>
)
:
(
<
Dropdown
actions=
{
<>
<
button
className=
"w-full text-left text-sm whitespace-nowrap leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handleChangePasswordClick
(
user
)
}
>
{
t
(
"setting.account-section.change-password"
)
}
</
button
>
{
user
.
rowStatus
===
"NORMAL"
?
(
<
button
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handleArchiveUserClick
(
user
)
}
>
{
t
(
"setting.member-section.archive-member"
)
}
</
button
>
)
:
(
<
div
className=
"w-full overflow-x-auto"
>
<
div
className=
"inline-block min-w-full align-middle"
>
<
table
className=
"min-w-full divide-y divide-gray-300"
>
<
thead
>
<
tr
>
<
th
scope=
"col"
className=
"py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900"
>
ID
</
th
>
<
th
scope=
"col"
className=
"px-3 py-2 text-left text-sm font-semibold text-gray-900"
>
{
t
(
"common.username"
)
}
</
th
>
<
th
scope=
"col"
className=
"px-3 py-2 text-left text-sm font-semibold text-gray-900"
>
{
t
(
"common.nickname"
)
}
</
th
>
<
th
scope=
"col"
className=
"px-3 py-2 text-left text-sm font-semibold text-gray-900"
>
{
t
(
"common.email"
)
}
</
th
>
<
th
scope=
"col"
className=
"relative py-2 pl-3 pr-4"
></
th
>
</
tr
>
</
thead
>
<
tbody
className=
"divide-y divide-gray-200"
>
{
userList
.
map
((
user
)
=>
(
<
tr
key=
{
user
.
id
}
>
<
td
className=
"whitespace-nowrap py-2 pl-4 pr-3 text-sm text-gray-900"
>
{
user
.
id
}
</
td
>
<
td
className=
"whitespace-nowrap px-3 py-2 text-sm text-gray-500"
>
{
user
.
username
}
</
td
>
<
td
className=
"whitespace-nowrap px-3 py-2 text-sm text-gray-500"
>
{
user
.
nickname
}
</
td
>
<
td
className=
"whitespace-nowrap px-3 py-2 text-sm text-gray-500"
>
{
user
.
email
}
</
td
>
<
td
className=
"relative whitespace-nowrap py-2 pl-3 pr-4 text-right text-sm font-medium flex justify-end"
>
{
currentUser
?.
id
===
user
.
id
?
(
<
span
>
{
t
(
"common.yourself"
)
}
</
span
>
)
:
(
<
Dropdown
actions=
{
<>
<
button
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handle
RestoreUser
Click
(
user
)
}
className=
"w-full text-left text-sm
whitespace-nowrap
leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handle
ChangePassword
Click
(
user
)
}
>
{
t
(
"common.restore"
)
}
</
button
>
<
button
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded text-red-600 hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handleDeleteUserClick
(
user
)
}
>
{
t
(
"setting.member-section.delete-member"
)
}
{
t
(
"setting.account-section.change-password"
)
}
</
button
>
{
user
.
rowStatus
===
"NORMAL"
?
(
<
button
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handleArchiveUserClick
(
user
)
}
>
{
t
(
"setting.member-section.archive-member"
)
}
</
button
>
)
:
(
<>
<
button
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handleRestoreUserClick
(
user
)
}
>
{
t
(
"common.restore"
)
}
</
button
>
<
button
className=
"w-full text-left text-sm leading-6 py-1 px-3 cursor-pointer rounded text-red-600 hover:bg-gray-100 dark:hover:bg-zinc-600"
onClick=
{
()
=>
handleDeleteUserClick
(
user
)
}
>
{
t
(
"setting.member-section.delete-member"
)
}
</
button
>
</>
)
}
</>
)
}
<
/>
}
/
>
)
}
</
td
>
</
t
r
>
))
}
</
tbody
>
</
Table
>
}
/>
)
}
</
td
>
</
tr
>
))
}
</
t
body
>
</
table
>
</
div
>
</
div
>
</
div
>
);
};
...
...
web/src/components/Settings/OpenAISection.tsx
0 → 100644
View file @
220cba84
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
<
OpenAIConfig
>
({
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
(
<
div
className=
"section-container system-section-container"
>
<
p
className=
"title-text"
>
{
t
(
"setting.openai"
)
}
</
p
>
<
div
className=
"form-label"
>
<
div
className=
"flex flex-row items-center"
>
<
span
className=
"text-sm mr-1"
>
{
t
(
"setting.system-section.openai-api-key"
)
}
</
span
>
<
LearnMore
title=
{
t
(
"setting.system-section.openai-api-key-description"
)
}
url=
"https://platform.openai.com/account/api-keys"
/>
</
div
>
</
div
>
<
Input
className=
"w-full"
sx=
{
{
fontFamily
:
"monospace"
,
fontSize
:
"14px"
,
}
}
placeholder=
{
t
(
"setting.system-section.openai-api-key-placeholder"
)
}
value=
{
openAIConfig
.
key
}
onChange=
{
(
event
)
=>
handleOpenAIConfigKeyChanged
(
event
.
target
.
value
)
}
/>
<
div
className=
"form-label mt-2"
>
<
span
className=
"normal-text"
>
{
t
(
"setting.system-section.openai-api-host"
)
}
</
span
>
</
div
>
<
Input
className=
"w-full"
sx=
{
{
fontFamily
:
"monospace"
,
fontSize
:
"14px"
,
}
}
placeholder=
{
t
(
"setting.system-section.openai-api-host-placeholder"
)
}
value=
{
openAIConfig
.
host
}
onChange=
{
(
event
)
=>
handleOpenAIConfigHostChanged
(
event
.
target
.
value
)
}
/>
<
div
className=
"mt-4"
>
<
Button
onClick=
{
handleSaveOpenAIConfig
}
>
{
t
(
"common.save"
)
}
</
Button
>
</
div
>
</
div
>
);
};
export
default
OpenAISection
;
web/src/components/UpdateCustomizedProfileDialog.tsx
View file @
220cba84
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<Props> = ({ destroy }: Props) => {
{
t
(
"setting.system-section.server-name"
)
}
<
span
className=
"text-sm text-gray-400 ml-1"
>
(
{
t
(
"setting.system-section.customize-server.default"
)
}
)
</
span
>
</
p
>
<
input
type=
"text"
className=
"input-
text"
value=
{
state
.
name
}
onChange=
{
handleNameChanged
}
/>
<
Input
className=
"w-full"
type=
"
text"
value=
{
state
.
name
}
onChange=
{
handleNameChanged
}
/>
<
p
className=
"text-sm mb-1 mt-2"
>
{
t
(
"setting.system-section.customize-server.icon-url"
)
}
</
p
>
<
input
type=
"text"
className=
"input-
text"
value=
{
state
.
logoUrl
}
onChange=
{
handleLogoUrlChanged
}
/>
<
Input
className=
"w-full"
type=
"
text"
value=
{
state
.
logoUrl
}
onChange=
{
handleLogoUrlChanged
}
/>
<
p
className=
"text-sm mb-1 mt-2"
>
{
t
(
"setting.system-section.customize-server.description"
)
}
</
p
>
<
Textarea
minRows=
"2"
maxRows=
"4"
className=
"!input-text"
value=
{
state
.
description
}
onChange=
{
handleDescriptionChanged
}
/>
<
p
className=
"text-sm mb-1 mt-2"
>
{
t
(
"setting.system-section.customize-server.locale"
)
}
</
p
>
...
...
@@ -114,17 +115,15 @@ const UpdateCustomizedProfileDialog: React.FC<Props> = ({ destroy }: Props) => {
<
AppearanceSelect
className=
"!w-full"
value=
{
state
.
appearance
}
onChange=
{
handleAppearanceSelectChange
}
/>
<
div
className=
"mt-4 w-full flex flex-row justify-between items-center space-x-2"
>
<
div
className=
"flex flex-row justify-start items-center"
>
<
button
className=
"btn-normal
"
onClick=
{
handleRestoreButtonClick
}
>
<
Button
variant=
"outlined
"
onClick=
{
handleRestoreButtonClick
}
>
{
t
(
"common.restore"
)
}
</
b
utton
>
</
B
utton
>
</
div
>
<
div
className=
"flex flex-row justify-end items-center"
>
<
button
className=
"btn-text
"
onClick=
{
handleCloseButtonClick
}
>
<
Button
variant=
"plain
"
onClick=
{
handleCloseButtonClick
}
>
{
t
(
"common.cancel"
)
}
</
button
>
<
button
className=
"btn-primary"
onClick=
{
handleSaveButtonClick
}
>
{
t
(
"common.save"
)
}
</
button
>
</
Button
>
<
Button
onClick=
{
handleSaveButtonClick
}
>
{
t
(
"common.save"
)
}
</
Button
>
</
div
>
</
div
>
</
div
>
...
...
web/src/css/tailwind.css
View file @
220cba84
...
...
@@ -21,7 +21,7 @@
@layer
components
{
.btn-normal
{
@apply
select-none
flex
flex-row
justify-center
items-center
border
dark
:
border-zinc-
7
00
cursor-pointer
px-3
text-sm
leading-8
rounded-md
hover
:
opacity-80
hover
:
shadow
disabled
:
cursor-not-allowed
disabled
:
opacity-60
disabled
:
hover
:
shadow-none
;
@apply
select-none
flex
flex-row
justify-center
items-center
border
dark
:
border-zinc-
8
00
cursor-pointer
px-3
text-sm
leading-8
rounded-md
hover
:
opacity-80
hover
:
shadow
disabled
:
cursor-not-allowed
disabled
:
opacity-60
disabled
:
hover
:
shadow-none
;
}
.btn-primary
{
...
...
web/src/less/settings/member-section.less
deleted
100644 → 0
View file @
032509dd
.member-section-container {
> .create-member-container {
@apply w-full flex flex-col justify-start items-start;
> .input-form-container {
@apply w-full mb-3 flex flex-row justify-start items-center;
> .field-text {
@apply text-sm text-gray-600 dark:text-gray-400 w-20 text-right pr-2;
}
> input {
@apply border dark:border-zinc-700 rounded text-sm leading-7 shadow-inner px-2 dark:bg-zinc-800;
}
}
> .btns-container {
@apply w-full mb-6 pl-20 flex flex-row justify-start items-center;
}
}
}
web/src/locales/en.json
View file @
220cba84
...
...
@@ -173,6 +173,7 @@
"member"
:
"Member"
,
"member-list"
:
"Member list"
,
"system"
:
"System"
,
"openai"
:
"OpenAI"
,
"storage"
:
"Storage"
,
"sso"
:
"SSO"
,
"account-section"
:
{
...
...
web/src/pages/Auth.tsx
View file @
220cba84
...
...
@@ -160,13 +160,13 @@ const Auth = () => {
{
actionBtnLoadingState
.
isLoading
&&
<
Icon
.
Loader
className=
"w-4 h-auto mr-2 animate-spin dark:text-gray-300"
/>
}
{
systemStatus
?.
allowSignUp
&&
(
<>
<
Button
variant=
{
"plain"
}
loading
=
{
actionBtnLoadingState
.
isLoading
}
onClick=
{
handleSignUpButtonClick
}
>
<
Button
variant=
{
"plain"
}
disabled
=
{
actionBtnLoadingState
.
isLoading
}
onClick=
{
handleSignUpButtonClick
}
>
{
t
(
"common.sign-up"
)
}
</
Button
>
<
span
className=
"mr-2 font-mono text-gray-200"
>
/
</
span
>
</>
)
}
<
Button
type=
"submit"
loading
=
{
actionBtnLoadingState
.
isLoading
}
onClick=
{
handleSignInButtonClick
}
>
<
Button
type=
"submit"
disabled
=
{
actionBtnLoadingState
.
isLoading
}
onClick=
{
handleSignInButtonClick
}
>
{
t
(
"common.sign-in"
)
}
</
Button
>
</
div
>
...
...
web/src/pages/Explore.tsx
View file @
220cba84
...
...
@@ -57,7 +57,10 @@ const Explore = () => {
})
:
memos
;
const
sortedMemos
=
shownMemos
.
filter
((
m
)
=>
m
.
rowStatus
===
"NORMAL"
&&
m
.
visibility
!==
"PRIVATE"
);
const
sortedMemos
=
shownMemos
.
filter
((
m
)
=>
m
.
rowStatus
===
"NORMAL"
&&
m
.
visibility
!==
"PRIVATE"
)
.
sort
((
mi
,
mj
)
=>
mj
.
displayTs
-
mi
.
displayTs
);
const
handleFetchMoreClick
=
async
()
=>
{
try
{
const
fetchedMemos
=
await
memoStore
.
fetchAllMemos
(
DEFAULT_MEMO_LIMIT
,
memos
.
length
);
...
...
web/src/pages/MemoChat.tsx
View file @
220cba84
...
...
@@ -119,18 +119,16 @@ const MemoChat = () => {
};
return
(
<
section
className=
"w-full max-w-
2
xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800"
>
<
section
className=
"w-full max-w-
3
xl min-h-full flex flex-col justify-start items-center px-4 sm:px-2 sm:pt-4 pb-8 bg-zinc-100 dark:bg-zinc-800"
>
<
MobileHeader
showSearch=
{
false
}
/>
<
div
className=
"w-full flex flex-col justify-start items-start px-4 py-3 rounded-xl bg-white dark:bg-zinc-700 text-black dark:text-gray-300"
>
<
div
className=
"flex space-x-2"
>
<
div
className=
"w-full flex flex-row justify-between items-center"
>
<
p
className=
"flex flex-row justify-start items-center select-none rounded"
>
<
Icon
.
Bot
className=
"w-5 h-auto mr-1"
/>
{
t
(
"memo-chat.title"
)
}
</
p
>
<
div
className=
"w-full flex"
>
<
div
className=
"w-auto flex flex-row justify-start items-center select-none shrink-0 mr-4"
>
<
Icon
.
Bot
className=
"w-5 h-auto mr-1"
/>
{
t
(
"memo-chat.title"
)
}
</
div
>
<
span
className=
"flex flex-row w-full justify-start items-center
"
>
<
div
className=
"flex space-x-2
max-w-md overflow-scroll
"
>
<
div
className=
"flex flex-row w-auto justify-start items-center overflow-y-hidden overflow-x-auto
"
>
<
div
className=
"flex space-x-2
overflow-x-auto
"
>
{
conversationList
.
map
((
item
:
Conversation
)
=>
(
<
ConversationTab
key=
{
item
.
messageStorageId
}
...
...
@@ -146,8 +144,7 @@ const MemoChat = () => {
/>
))
}
</
div
>
<
button
className=
"btn-text px-1 ml-1"
>
<
button
className=
"btn-text px-1 ml-1 shrink-0"
>
<
Icon
.
Plus
className=
"w-4 h-auto"
onClick=
{
()
=>
{
...
...
@@ -155,7 +152,7 @@ const MemoChat = () => {
}
}
/>
</
button
>
</
span
>
</
div
>
</
div
>
<
div
className=
"dialog-content-container w-full"
>
...
...
web/src/pages/Setting.tsx
View file @
220cba84
import
{
Option
,
Select
}
from
"@mui/joy"
;
import
{
useState
}
from
"react"
;
import
{
useTranslate
}
from
"@/utils/i18n"
;
import
{
useUserStore
}
from
"@/store/module"
;
import
{
use
GlobalStore
,
use
UserStore
}
from
"@/store/module"
;
import
Icon
from
"@/components/Icon"
;
import
BetaBadge
from
"@/components/BetaBadge"
;
import
MyAccountSection
from
"@/components/Settings/MyAccountSection"
;
import
PreferencesSection
from
"@/components/Settings/PreferencesSection"
;
import
MemberSection
from
"@/components/Settings/MemberSection"
;
import
SystemSection
from
"@/components/Settings/SystemSection"
;
import
OpenAISection
from
"@/components/Settings/OpenAISection"
;
import
StorageSection
from
"@/components/Settings/StorageSection"
;
import
SSOSection
from
"@/components/Settings/SSOSection"
;
import
MobileHeader
from
"@/components/MobileHeader"
;
import
"@/less/setting.less"
;
type
SettingSection
=
"my-account"
|
"preference"
|
"member"
|
"system"
|
"storage"
|
"sso"
;
type
SettingSection
=
"my-account"
|
"preference"
|
"member"
|
"system"
|
"
openai"
|
"
storage"
|
"sso"
;
interface
State
{
selectedSection
:
SettingSection
;
...
...
@@ -21,6 +22,7 @@ interface State {
const
Setting
=
()
=>
{
const
t
=
useTranslate
();
const
globalStore
=
useGlobalStore
();
const
userStore
=
useUserStore
();
const
user
=
userStore
.
state
.
user
;
const
[
state
,
setState
]
=
useState
<
State
>
({
...
...
@@ -37,7 +39,11 @@ const Setting = () => {
const
getSettingSectionList
=
()
=>
{
let
settingList
:
SettingSection
[]
=
[
"my-account"
,
"preference"
];
if
(
isHost
)
{
settingList
=
settingList
.
concat
([
"member"
,
"system"
,
"storage"
,
"sso"
]);
if
(
globalStore
.
isDev
())
{
settingList
=
settingList
.
concat
([
"member"
,
"system"
,
"openai"
,
"storage"
,
"sso"
]);
}
else
{
settingList
=
settingList
.
concat
([
"member"
,
"system"
,
"storage"
,
"sso"
]);
}
}
return
settingList
;
};
...
...
@@ -78,6 +84,14 @@ const Setting = () => {
>
<
Icon
.
Settings2
className=
"w-4 h-auto mr-2 opacity-80"
/>
{
t
(
"setting.system"
)
}
</
span
>
{
globalStore
.
isDev
()
&&
(
<
span
onClick=
{
()
=>
handleSectionSelectorItemClick
(
"openai"
)
}
className=
{
`section-item ${state.selectedSection === "openai" ? "selected" : ""}`
}
>
<
Icon
.
Bot
className=
"w-4 h-auto mr-2 opacity-80"
/>
{
t
(
"setting.openai"
)
}
<
BetaBadge
/>
</
span
>
)
}
<
span
onClick=
{
()
=>
handleSectionSelectorItemClick
(
"storage"
)
}
className=
{
`section-item ${state.selectedSection === "storage" ? "selected" : ""}`
}
...
...
@@ -114,6 +128,8 @@ const Setting = () => {
<
MemberSection
/>
)
:
state
.
selectedSection
===
"system"
?
(
<
SystemSection
/>
)
:
state
.
selectedSection
===
"openai"
?
(
<
OpenAISection
/>
)
:
state
.
selectedSection
===
"storage"
?
(
<
StorageSection
/>
)
:
state
.
selectedSection
===
"sso"
?
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment