Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
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
ai-tech
dify
Commits
477f1b37
Commit
477f1b37
authored
Jul 09, 2023
by
JzoNg
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: invitation and activate
parent
20f6aa25
Changes
24
Hide whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
607 additions
and
202 deletions
+607
-202
activateForm.tsx
web/app/activate/activateForm.tsx
+231
-0
page.tsx
web/app/activate/page.tsx
+32
-0
style.module.css
web/app/activate/style.module.css
+4
-0
team-28x28.png
web/app/activate/team-28x28.png
+0
-0
locale.tsx
web/app/components/base/select/locale.tsx
+13
-12
index.tsx
.../components/header/account-setting/members-page/index.tsx
+4
-1
index.tsx
...eader/account-setting/members-page/invite-modal/index.tsx
+12
-12
copied.svg
...ount-setting/members-page/invited-modal/assets/copied.svg
+3
-0
copy-hover.svg
...-setting/members-page/invited-modal/assets/copy-hover.svg
+3
-0
copy.svg
...ccount-setting/members-page/invited-modal/assets/copy.svg
+3
-0
index.module.css
...count-setting/members-page/invited-modal/index.module.css
+16
-0
index.tsx
...ader/account-setting/members-page/invited-modal/index.tsx
+15
-9
invitation-link.tsx
...nt-setting/members-page/invited-modal/invitation-link.tsx
+63
-0
installForm.tsx
web/app/install/installForm.tsx
+33
-37
_header.tsx
web/app/signin/_header.tsx
+1
-7
forms.tsx
web/app/signin/forms.tsx
+2
-3
normalForm.tsx
web/app/signin/normalForm.tsx
+22
-22
oneMoreStep.tsx
web/app/signin/oneMoreStep.tsx
+22
-12
page.tsx
web/app/signin/page.tsx
+5
-6
common.en.ts
web/i18n/lang/common.en.ts
+4
-3
common.zh.ts
web/i18n/lang/common.zh.ts
+3
-2
login.en.ts
web/i18n/lang/login.en.ts
+53
-37
login.zh.ts
web/i18n/lang/login.zh.ts
+53
-37
common.ts
web/service/common.ts
+10
-2
No files found.
web/app/activate/activateForm.tsx
0 → 100644
View file @
477f1b37
'use client'
import
{
useState
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
useSWR
from
'swr'
import
{
useSearchParams
}
from
'next/navigation'
import
cn
from
'classnames'
import
Link
from
'next/link'
import
{
CheckCircleIcon
}
from
'@heroicons/react/24/solid'
import
style
from
'./style.module.css'
import
Button
from
'@/app/components/base/button'
import
{
SimpleSelect
}
from
'@/app/components/base/select'
import
{
timezones
}
from
'@/utils/timezone'
import
{
languageMaps
,
languages
}
from
'@/utils/language'
import
{
activateMember
,
invitationCheck
}
from
'@/service/common'
import
Toast
from
'@/app/components/base/toast'
import
Loading
from
'@/app/components/base/loading'
const
validPassword
=
/^
(?=
.*
[
a-zA-Z
])(?=
.*
\d)
.
{8,}
$/
const
ActivateForm
=
()
=>
{
const
{
t
}
=
useTranslation
()
const
searchParams
=
useSearchParams
()
const
workspaceID
=
searchParams
.
get
(
'workspace_id'
)
const
email
=
searchParams
.
get
(
'email'
)
const
token
=
searchParams
.
get
(
'token'
)
const
checkParams
=
{
url
:
'/activate/check'
,
params
:
{
workspace_id
:
workspaceID
,
email
,
token
,
},
}
const
{
data
:
checkRes
,
mutate
:
recheck
}
=
useSWR
(
checkParams
,
invitationCheck
)
const
[
name
,
setName
]
=
useState
(
''
)
const
[
password
,
setPassword
]
=
useState
(
''
)
const
[
timezone
,
setTimezone
]
=
useState
(
'Asia/Shanghai'
)
const
[
language
,
setLanguage
]
=
useState
(
'en-US'
)
const
[
showSuccess
,
setShowSuccess
]
=
useState
(
false
)
const
showErrorMessage
=
(
message
:
string
)
=>
{
Toast
.
notify
({
type
:
'error'
,
message
,
})
}
const
valid
=
()
=>
{
if
(
!
name
.
trim
())
{
showErrorMessage
(
t
(
'login.error.nameEmpty'
))
return
false
}
if
(
!
password
.
trim
())
{
showErrorMessage
(
t
(
'login.error.passwordEmpty'
))
return
false
}
if
(
!
validPassword
.
test
(
password
))
showErrorMessage
(
t
(
'login.error.passwordInvalid'
))
return
true
}
const
handleActivate
=
async
()
=>
{
if
(
!
valid
())
return
try
{
await
activateMember
({
url
:
'/activate'
,
body
:
{
workspace_id
:
workspaceID
,
email
,
token
,
name
,
password
,
interface_language
:
language
,
timezone
,
},
})
setShowSuccess
(
true
)
}
catch
{
recheck
()
}
}
return
(
<
div
className=
{
cn
(
'flex flex-col items-center w-full grow items-center justify-center'
,
'px-6'
,
'md:px-[108px]'
,
)
}
>
{
!
checkRes
&&
<
Loading
/>
}
{
checkRes
&&
!
checkRes
.
is_valid
&&
(
<
div
className=
"flex flex-col md:w-[400px]"
>
<
div
className=
"w-full mx-auto"
>
<
div
className=
"mb-3 flex justify-center items-center w-20 h-20 p-5 rounded-[20px] border border-gray-100 shadow-lg text-[40px] font-bold"
>
🤷♂️
</
div
>
<
h2
className=
"text-[32px] font-bold text-gray-900"
>
{
t
(
'login.invalid'
)
}
</
h2
>
</
div
>
<
div
className=
"w-full mx-auto mt-6"
>
<
Button
type=
'primary'
className=
'w-full !fone-medium !text-sm'
>
<
a
href=
"https://dify.ai"
>
{
t
(
'login.explore'
)
}
</
a
>
</
Button
>
</
div
>
</
div
>
)
}
{
checkRes
&&
checkRes
.
is_valid
&&
!
showSuccess
&&
(
<
div
className=
'flex flex-col md:w-[400px]'
>
<
div
className=
"w-full mx-auto"
>
<
div
className=
{
`mb-3 flex justify-center items-center w-20 h-20 p-5 rounded-[20px] border border-gray-100 shadow-lg text-[40px] font-bold ${style.logo}`
}
>
</
div
>
<
h2
className=
"text-[32px] font-bold text-gray-900"
>
{
`${t('login.join')} ${checkRes.workspace_name}`
}
</
h2
>
<
p
className=
'mt-1 text-sm text-gray-600 '
>
{
`${t('login.joinTipStart')} ${checkRes.workspace_name} ${t('login.joinTipEnd')}`
}
</
p
>
</
div
>
<
div
className=
"w-full mx-auto mt-6"
>
<
div
className=
"bg-white"
>
{
/* username */
}
<
div
className=
'mb-5'
>
<
label
htmlFor=
"name"
className=
"my-2 flex items-center justify-between text-sm font-medium text-gray-900"
>
{
t
(
'login.name'
)
}
</
label
>
<
div
className=
"mt-1 relative rounded-md shadow-sm"
>
<
input
id=
"name"
type=
"text"
value=
{
name
}
onChange=
{
e
=>
setName
(
e
.
target
.
value
)
}
placeholder=
{
t
(
'login.namePlaceholder'
)
||
''
}
className=
{
'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm pr-10'
}
/>
</
div
>
</
div
>
{
/* password */
}
<
div
className=
'mb-5'
>
<
label
htmlFor=
"password"
className=
"my-2 flex items-center justify-between text-sm font-medium text-gray-900"
>
{
t
(
'login.password'
)
}
</
label
>
<
div
className=
"mt-1 relative rounded-md shadow-sm"
>
<
input
id=
"password"
type=
'password'
value=
{
password
}
onChange=
{
e
=>
setPassword
(
e
.
target
.
value
)
}
placeholder=
{
t
(
'login.passwordPlaceholder'
)
||
''
}
className=
{
'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm pr-10'
}
/>
</
div
>
<
div
className=
'mt-1 text-xs text-gray-500'
>
{
t
(
'login.error.passwordInvalid'
)
}
</
div
>
</
div
>
{
/* language */
}
<
div
className=
'mb-5'
>
<
label
htmlFor=
"name"
className=
"my-2 flex items-center justify-between text-sm font-medium text-gray-900"
>
{
t
(
'login.interfaceLanguage'
)
}
</
label
>
<
div
className=
"relative mt-1 rounded-md shadow-sm"
>
<
SimpleSelect
defaultValue=
{
languageMaps
.
en
}
items=
{
languages
}
onSelect=
{
(
item
)
=>
{
setLanguage
(
item
.
value
as
string
)
}
}
/>
</
div
>
</
div
>
{
/* timezone */
}
<
div
className=
'mb-4'
>
<
label
htmlFor=
"timezone"
className=
"block text-sm font-medium text-gray-700"
>
{
t
(
'login.timezone'
)
}
</
label
>
<
div
className=
"relative mt-1 rounded-md shadow-sm"
>
<
SimpleSelect
defaultValue=
{
timezone
}
items=
{
timezones
}
onSelect=
{
(
item
)
=>
{
setTimezone
(
item
.
value
as
string
)
}
}
/>
</
div
>
</
div
>
<
div
>
<
Button
type=
'primary'
className=
'w-full !fone-medium !text-sm'
onClick=
{
handleActivate
}
>
{
`${t('login.join')} ${checkRes.workspace_name}`
}
</
Button
>
</
div
>
<
div
className=
"block w-hull mt-2 text-xs text-gray-600"
>
{
t
(
'login.license.tip'
)
}
<
Link
className=
'text-primary-600'
target=
{
'_blank'
}
href=
'https://docs.dify.ai/community/open-source'
>
{
t
(
'login.license.link'
)
}
</
Link
>
</
div
>
</
div
>
</
div
>
</
div
>
)
}
{
checkRes
&&
checkRes
.
is_valid
&&
showSuccess
&&
(
<
div
className=
"flex flex-col md:w-[400px]"
>
<
div
className=
"w-full mx-auto"
>
<
div
className=
"mb-3 flex justify-center items-center w-20 h-20 p-5 rounded-[20px] border border-gray-100 shadow-lg text-[40px] font-bold"
>
<
CheckCircleIcon
className=
'w-10 h-10 text-[#039855]'
/>
</
div
>
<
h2
className=
"text-[32px] font-bold text-gray-900"
>
{
`${t('login.activatedTipStart')} ${checkRes.workspace_name} ${t('login.activatedTipEnd')}`
}
</
h2
>
</
div
>
<
div
className=
"w-full mx-auto mt-6"
>
<
Button
type=
'primary'
className=
'w-full !fone-medium !text-sm'
>
<
a
href=
"/signin"
>
{
t
(
'login.activated'
)
}
</
a
>
</
Button
>
</
div
>
</
div
>
)
}
</
div
>
)
}
export
default
ActivateForm
web/app/activate/page.tsx
0 → 100644
View file @
477f1b37
import
React
from
'react'
import
cn
from
'classnames'
import
Header
from
'../signin/_header'
import
style
from
'../signin/page.module.css'
import
ActivateForm
from
'./activateForm'
const
Activate
=
()
=>
{
return
(
<
div
className=
{
cn
(
style
.
background
,
'flex w-full min-h-screen'
,
'sm:p-4 lg:p-8'
,
'gap-x-20'
,
'justify-center lg:justify-start'
,
)
}
>
<
div
className=
{
cn
(
'flex w-full flex-col bg-white shadow rounded-2xl shrink-0'
,
'space-between'
,
)
}
>
<
Header
/>
<
ActivateForm
/>
<
div
className=
'px-8 py-6 text-sm font-normal text-gray-500'
>
©
{
new
Date
().
getFullYear
()
}
Dify, Inc. All rights reserved.
</
div
>
</
div
>
</
div
>
)
}
export
default
Activate
web/app/activate/style.module.css
0 → 100644
View file @
477f1b37
.logo
{
background
:
#fff
center
no-repeat
url(./team-28x28.png)
;
background-size
:
56px
;
}
\ No newline at end of file
web/app/activate/team-28x28.png
0 → 100644
View file @
477f1b37
7.18 KB
web/app/components/base/select/locale.tsx
View file @
477f1b37
...
@@ -11,7 +11,7 @@ export const RFC_LOCALES = [
...
@@ -11,7 +11,7 @@ export const RFC_LOCALES = [
{
value
:
'en-US'
,
name
:
'EN'
},
{
value
:
'en-US'
,
name
:
'EN'
},
{
value
:
'zh-Hans'
,
name
:
'简体中文'
},
{
value
:
'zh-Hans'
,
name
:
'简体中文'
},
]
]
interface
ISelectProps
{
type
ISelectProps
=
{
items
:
Array
<
{
value
:
string
;
name
:
string
}
>
items
:
Array
<
{
value
:
string
;
name
:
string
}
>
value
?:
string
value
?:
string
className
?:
string
className
?:
string
...
@@ -21,7 +21,7 @@ interface ISelectProps {
...
@@ -21,7 +21,7 @@ interface ISelectProps {
export
default
function
Select
({
export
default
function
Select
({
items
,
items
,
value
,
value
,
onChange
onChange
,
}:
ISelectProps
)
{
}:
ISelectProps
)
{
const
item
=
items
.
filter
(
item
=>
item
.
value
===
value
)[
0
]
const
item
=
items
.
filter
(
item
=>
item
.
value
===
value
)[
0
]
...
@@ -29,11 +29,12 @@ export default function Select({
...
@@ -29,11 +29,12 @@ export default function Select({
<
div
className=
"w-56 text-right"
>
<
div
className=
"w-56 text-right"
>
<
Menu
as=
"div"
className=
"relative inline-block text-left"
>
<
Menu
as=
"div"
className=
"relative inline-block text-left"
>
<
div
>
<
div
>
<
Menu
.
Button
className=
"inline-flex w-full justify-center items-center
<
Menu
.
Button
className=
"inline-flex w-full h-[44px]justify-center items-center
rounded-lg px-2 py-1
rounded-lg px-[10px] py-[6px]
text-gray-600 text-xs font-medium
text-gray-900 text-[13px] font-medium
border border-gray-200"
>
border border-gray-200
<
GlobeAltIcon
className=
"w-5 h-5 mr-2 "
aria
-
hidden=
"true"
/>
hover:bg-gray-100"
>
<
GlobeAltIcon
className=
"w-5 h-5 mr-1"
aria
-
hidden=
"true"
/>
{
item
?.
name
}
{
item
?.
name
}
</
Menu
.
Button
>
</
Menu
.
Button
>
</
div
>
</
div
>
...
@@ -46,14 +47,14 @@ export default function Select({
...
@@ -46,14 +47,14 @@ export default function Select({
leaveFrom=
"transform opacity-100 scale-100"
leaveFrom=
"transform opacity-100 scale-100"
leaveTo=
"transform opacity-0 scale-95"
leaveTo=
"transform opacity-0 scale-95"
>
>
<
Menu
.
Items
className=
"absolute right-0 mt-2 w-
28
origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
>
<
Menu
.
Items
className=
"absolute right-0 mt-2 w-
[120px]
origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
>
<
div
className=
"px-1 py-1 "
>
<
div
className=
"px-1 py-1 "
>
{
items
.
map
((
item
)
=>
{
{
items
.
map
((
item
)
=>
{
return
<
Menu
.
Item
key=
{
item
.
value
}
>
return
<
Menu
.
Item
key=
{
item
.
value
}
>
{
({
active
})
=>
(
{
({
active
})
=>
(
<
button
<
button
className=
{
`${active ? 'bg-gray-100' : ''
className=
{
`${active ? 'bg-gray-100' : ''
} group flex w-full items-center rounded-md px-2 py-2 text-sm
`
}
} group flex w-full items-center rounded-lg px-3 py-2 text-sm text-gray-700
`
}
onClick=
{
(
evt
)
=>
{
onClick=
{
(
evt
)
=>
{
evt
.
preventDefault
()
evt
.
preventDefault
()
onChange
&&
onChange
(
item
.
value
)
onChange
&&
onChange
(
item
.
value
)
...
@@ -77,7 +78,7 @@ export default function Select({
...
@@ -77,7 +78,7 @@ export default function Select({
export
function
InputSelect
({
export
function
InputSelect
({
items
,
items
,
value
,
value
,
onChange
onChange
,
}:
ISelectProps
)
{
}:
ISelectProps
)
{
const
item
=
items
.
filter
(
item
=>
item
.
value
===
value
)[
0
]
const
item
=
items
.
filter
(
item
=>
item
.
value
===
value
)[
0
]
return
(
return
(
...
@@ -104,7 +105,7 @@ export function InputSelect({
...
@@ -104,7 +105,7 @@ export function InputSelect({
{
({
active
})
=>
(
{
({
active
})
=>
(
<
button
<
button
className=
{
`${active ? 'bg-gray-100' : ''
className=
{
`${active ? 'bg-gray-100' : ''
} group flex w-full items-center rounded-md px-2 py-2 text-sm`
}
} group flex w-full items-center rounded-md px-2 py-2 text-sm`
}
onClick=
{
()
=>
{
onClick=
{
()
=>
{
onChange
&&
onChange
(
item
.
value
)
onChange
&&
onChange
(
item
.
value
)
}
}
}
}
...
@@ -122,4 +123,4 @@ export function InputSelect({
...
@@ -122,4 +123,4 @@ export function InputSelect({
</
Menu
>
</
Menu
>
</
div
>
</
div
>
)
)
}
}
\ No newline at end of file
web/app/components/header/account-setting/members-page/index.tsx
View file @
477f1b37
...
@@ -30,6 +30,7 @@ const MembersPage = () => {
...
@@ -30,6 +30,7 @@ const MembersPage = () => {
const
{
userProfile
}
=
useAppContext
()
const
{
userProfile
}
=
useAppContext
()
const
{
data
,
mutate
}
=
useSWR
({
url
:
'/workspaces/current/members'
},
fetchMembers
)
const
{
data
,
mutate
}
=
useSWR
({
url
:
'/workspaces/current/members'
},
fetchMembers
)
const
[
inviteModalVisible
,
setInviteModalVisible
]
=
useState
(
false
)
const
[
inviteModalVisible
,
setInviteModalVisible
]
=
useState
(
false
)
const
[
invitationLink
,
setInvitationLink
]
=
useState
(
''
)
const
[
invitedModalVisible
,
setInvitedModalVisible
]
=
useState
(
false
)
const
[
invitedModalVisible
,
setInvitedModalVisible
]
=
useState
(
false
)
const
accounts
=
data
?.
accounts
||
[]
const
accounts
=
data
?.
accounts
||
[]
const
owner
=
accounts
.
filter
(
account
=>
account
.
role
===
'owner'
)?.[
0
]?.
email
===
userProfile
.
email
const
owner
=
accounts
.
filter
(
account
=>
account
.
role
===
'owner'
)?.[
0
]?.
email
===
userProfile
.
email
...
@@ -93,8 +94,9 @@ const MembersPage = () => {
...
@@ -93,8 +94,9 @@ const MembersPage = () => {
inviteModalVisible
&&
(
inviteModalVisible
&&
(
<
InviteModal
<
InviteModal
onCancel=
{
()
=>
setInviteModalVisible
(
false
)
}
onCancel=
{
()
=>
setInviteModalVisible
(
false
)
}
onSend=
{
()
=>
{
onSend=
{
(
url
)
=>
{
setInvitedModalVisible
(
true
)
setInvitedModalVisible
(
true
)
setInvitationLink
(
url
)
mutate
()
mutate
()
}
}
}
}
/>
/>
...
@@ -103,6 +105,7 @@ const MembersPage = () => {
...
@@ -103,6 +105,7 @@ const MembersPage = () => {
{
{
invitedModalVisible
&&
(
invitedModalVisible
&&
(
<
InvitedModal
<
InvitedModal
invitationLink=
{
invitationLink
}
onCancel=
{
()
=>
setInvitedModalVisible
(
false
)
}
onCancel=
{
()
=>
setInvitedModalVisible
(
false
)
}
/>
/>
)
)
...
...
web/app/components/header/account-setting/members-page/invite-modal/index.tsx
View file @
477f1b37
...
@@ -3,16 +3,16 @@ import { useState } from 'react'
...
@@ -3,16 +3,16 @@ import { useState } from 'react'
import
{
useContext
}
from
'use-context-selector'
import
{
useContext
}
from
'use-context-selector'
import
{
XMarkIcon
}
from
'@heroicons/react/24/outline'
import
{
XMarkIcon
}
from
'@heroicons/react/24/outline'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
s
from
'./index.module.css'
import
Modal
from
'@/app/components/base/modal'
import
Modal
from
'@/app/components/base/modal'
import
Button
from
'@/app/components/base/button'
import
Button
from
'@/app/components/base/button'
import
s
from
'./index.module.css'
import
{
inviteMember
}
from
'@/service/common'
import
{
inviteMember
}
from
'@/service/common'
import
{
emailRegex
}
from
'@/config'
import
{
emailRegex
}
from
'@/config'
import
{
ToastContext
}
from
'@/app/components/base/toast'
import
{
ToastContext
}
from
'@/app/components/base/toast'
interface
IInviteModalProps
{
type
IInviteModalProps
=
{
onCancel
:
()
=>
void
,
onCancel
:
()
=>
void
onSend
:
(
)
=>
void
,
onSend
:
(
url
:
string
)
=>
void
}
}
const
InviteModal
=
({
const
InviteModal
=
({
onCancel
,
onCancel
,
...
@@ -25,16 +25,16 @@ const InviteModal = ({
...
@@ -25,16 +25,16 @@ const InviteModal = ({
const
handleSend
=
async
()
=>
{
const
handleSend
=
async
()
=>
{
if
(
emailRegex
.
test
(
email
))
{
if
(
emailRegex
.
test
(
email
))
{
try
{
try
{
const
res
=
await
inviteMember
({
url
:
'/workspaces/current/members/invite-email'
,
body
:
{
email
,
role
:
'admin'
}
})
const
res
=
await
inviteMember
({
url
:
'/workspaces/current/members/invite-email'
,
body
:
{
email
,
role
:
'admin'
}
})
if
(
res
.
result
===
'success'
)
{
if
(
res
.
result
===
'success'
)
{
onCancel
()
onCancel
()
onSend
()
onSend
(
res
.
invite_url
)
}
}
}
catch
(
e
)
{
}
}
}
else
{
catch
(
e
)
{}
}
else
{
notify
({
type
:
'error'
,
message
:
t
(
'common.members.emailInvalid'
)
})
notify
({
type
:
'error'
,
message
:
t
(
'common.members.emailInvalid'
)
})
}
}
}
}
...
@@ -51,15 +51,15 @@ const InviteModal = ({
...
@@ -51,15 +51,15 @@ const InviteModal = ({
<
div
className=
'mb-2 text-sm font-medium text-gray-900'
>
{
t
(
'common.members.email'
)
}
</
div
>
<
div
className=
'mb-2 text-sm font-medium text-gray-900'
>
{
t
(
'common.members.email'
)
}
</
div
>
<
input
<
input
className=
'
className=
'
block w-full py-2 mb-9 px-3 bg-gray-50 outline-none border-none
block w-full py-2 mb-9 px-3 bg-gray-50 outline-none border-none
appearance-none text-sm text-gray-900 rounded-lg
appearance-none text-sm text-gray-900 rounded-lg
'
'
value=
{
email
}
value=
{
email
}
onChange=
{
e
=>
setEmail
(
e
.
target
.
value
)
}
onChange=
{
e
=>
setEmail
(
e
.
target
.
value
)
}
placeholder=
{
t
(
'common.members.emailPlaceholder'
)
||
''
}
placeholder=
{
t
(
'common.members.emailPlaceholder'
)
||
''
}
/>
/>
<
Button
<
Button
className=
'w-full text-sm font-medium'
className=
'w-full text-sm font-medium'
onClick=
{
handleSend
}
onClick=
{
handleSend
}
type=
'primary'
type=
'primary'
>
>
...
...
web/app/components/header/account-setting/members-page/invited-modal/assets/copied.svg
0 → 100644
View file @
477f1b37
<svg
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M10.6665 2.66683C11.2865 2.66683 11.5965 2.66683 11.8508 2.73498C12.541 2.91991 13.0801 3.45901 13.265 4.14919C13.3332 4.40352 13.3332 4.71352 13.3332 5.3335V11.4668C13.3332 12.5869 13.3332 13.147 13.1152 13.5748C12.9234 13.9511 12.6175 14.2571 12.2412 14.4488C11.8133 14.6668 11.2533 14.6668 10.1332 14.6668H5.8665C4.7464 14.6668 4.18635 14.6668 3.75852 14.4488C3.3822 14.2571 3.07624 13.9511 2.88449 13.5748C2.6665 13.147 2.6665 12.5869 2.6665 11.4668V5.3335C2.6665 4.71352 2.6665 4.40352 2.73465 4.14919C2.91959 3.45901 3.45868 2.91991 4.14887 2.73498C4.4032 2.66683 4.71319 2.66683 5.33317 2.66683M5.99984 10.0002L7.33317 11.3335L10.3332 8.3335M6.39984 4.00016H9.59984C9.9732 4.00016 10.1599 4.00016 10.3025 3.9275C10.4279 3.86359 10.5299 3.7616 10.5938 3.63616C10.6665 3.49355 10.6665 3.30686 10.6665 2.9335V2.40016C10.6665 2.02679 10.6665 1.84011 10.5938 1.6975C10.5299 1.57206 10.4279 1.47007 10.3025 1.40616C10.1599 1.3335 9.97321 1.3335 9.59984 1.3335H6.39984C6.02647 1.3335 5.83978 1.3335 5.69718 1.40616C5.57174 1.47007 5.46975 1.57206 5.40583 1.6975C5.33317 1.84011 5.33317 2.02679 5.33317 2.40016V2.9335C5.33317 3.30686 5.33317 3.49355 5.40583 3.63616C5.46975 3.7616 5.57174 3.86359 5.69718 3.9275C5.83978 4.00016 6.02647 4.00016 6.39984 4.00016Z"
stroke=
"#1D2939"
stroke-width=
"1.5"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</svg>
web/app/components/header/account-setting/members-page/invited-modal/assets/copy-hover.svg
0 → 100644
View file @
477f1b37
<svg
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M10.6665 2.66634H11.9998C12.3535 2.66634 12.6926 2.80682 12.9426 3.05687C13.1927 3.30691 13.3332 3.64605 13.3332 3.99967V13.333C13.3332 13.6866 13.1927 14.0258 12.9426 14.2758C12.6926 14.5259 12.3535 14.6663 11.9998 14.6663H3.99984C3.64622 14.6663 3.30708 14.5259 3.05703 14.2758C2.80698 14.0258 2.6665 13.6866 2.6665 13.333V3.99967C2.6665 3.64605 2.80698 3.30691 3.05703 3.05687C3.30708 2.80682 3.64622 2.66634 3.99984 2.66634H5.33317M5.99984 1.33301H9.99984C10.368 1.33301 10.6665 1.63148 10.6665 1.99967V3.33301C10.6665 3.7012 10.368 3.99967 9.99984 3.99967H5.99984C5.63165 3.99967 5.33317 3.7012 5.33317 3.33301V1.99967C5.33317 1.63148 5.63165 1.33301 5.99984 1.33301Z"
stroke=
"#1D2939"
stroke-width=
"1.5"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</svg>
web/app/components/header/account-setting/members-page/invited-modal/assets/copy.svg
0 → 100644
View file @
477f1b37
<svg
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M10.6665 2.66634H11.9998C12.3535 2.66634 12.6926 2.80682 12.9426 3.05687C13.1927 3.30691 13.3332 3.64605 13.3332 3.99967V13.333C13.3332 13.6866 13.1927 14.0258 12.9426 14.2758C12.6926 14.5259 12.3535 14.6663 11.9998 14.6663H3.99984C3.64622 14.6663 3.30708 14.5259 3.05703 14.2758C2.80698 14.0258 2.6665 13.6866 2.6665 13.333V3.99967C2.6665 3.64605 2.80698 3.30691 3.05703 3.05687C3.30708 2.80682 3.64622 2.66634 3.99984 2.66634H5.33317M5.99984 1.33301H9.99984C10.368 1.33301 10.6665 1.63148 10.6665 1.99967V3.33301C10.6665 3.7012 10.368 3.99967 9.99984 3.99967H5.99984C5.63165 3.99967 5.33317 3.7012 5.33317 3.33301V1.99967C5.33317 1.63148 5.63165 1.33301 5.99984 1.33301Z"
stroke=
"#667085"
stroke-width=
"1.5"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</svg>
web/app/components/header/account-setting/members-page/invited-modal/index.module.css
View file @
477f1b37
...
@@ -2,4 +2,20 @@
...
@@ -2,4 +2,20 @@
padding
:
32px
!important
;
padding
:
32px
!important
;
width
:
480px
!important
;
width
:
480px
!important
;
background
:
linear-gradient
(
180deg
,
rgba
(
3
,
152
,
85
,
0.05
)
0%
,
rgba
(
3
,
152
,
85
,
0
)
22.44%
),
#F9FAFB
!important
;
background
:
linear-gradient
(
180deg
,
rgba
(
3
,
152
,
85
,
0.05
)
0%
,
rgba
(
3
,
152
,
85
,
0
)
22.44%
),
#F9FAFB
!important
;
}
.copyIcon
{
background-image
:
url(./assets/copy.svg)
;
background-position
:
center
;
background-repeat
:
no-repeat
;
}
.copyIcon
:hover
{
background-image
:
url(./assets/copy-hover.svg)
;
background-position
:
center
;
background-repeat
:
no-repeat
;
}
.copyIcon.copied
{
background-image
:
url(./assets/copied.svg)
;
}
}
\ No newline at end of file
web/app/components/header/account-setting/members-page/invited-modal/index.tsx
View file @
477f1b37
import
{
CheckCircleIcon
}
from
'@heroicons/react/24/solid'
import
{
CheckCircleIcon
}
from
'@heroicons/react/24/solid'
import
{
XMarkIcon
}
from
'@heroicons/react/24/outline'
import
{
XMarkIcon
}
from
'@heroicons/react/24/outline'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
InvitationLink
from
'./invitation-link'
import
s
from
'./index.module.css'
import
Modal
from
'@/app/components/base/modal'
import
Modal
from
'@/app/components/base/modal'
import
Button
from
'@/app/components/base/button'
import
Button
from
'@/app/components/base/button'
import
s
from
'./index.module.css'
type
IInvitedModalProps
=
{
invitationLink
:
string
interface
IInvitedModalProps
{
onCancel
:
()
=>
void
onCancel
:
()
=>
void
,
}
}
const
InvitedModal
=
({
const
InvitedModal
=
({
onCancel
invitationLink
,
onCancel
,
}:
IInvitedModalProps
)
=>
{
}:
IInvitedModalProps
)
=>
{
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
...
@@ -27,10 +29,14 @@ const InvitedModal = ({
...
@@ -27,10 +29,14 @@ const InvitedModal = ({
<
XMarkIcon
className=
'w-4 h-4 cursor-pointer'
onClick=
{
onCancel
}
/>
<
XMarkIcon
className=
'w-4 h-4 cursor-pointer'
onClick=
{
onCancel
}
/>
</
div
>
</
div
>
<
div
className=
'mb-1 text-xl font-semibold text-gray-900'
>
{
t
(
'common.members.invitationSent'
)
}
</
div
>
<
div
className=
'mb-1 text-xl font-semibold text-gray-900'
>
{
t
(
'common.members.invitationSent'
)
}
</
div
>
<
div
className=
'mb-10 text-sm text-gray-500'
>
{
t
(
'common.members.invitationSentTip'
)
}
</
div
>
<
div
className=
'mb-5 text-sm text-gray-500'
>
{
t
(
'common.members.invitationSentTip'
)
}
</
div
>
<
div
className=
'mb-9'
>
<
div
className=
'py-2 text-sm font-Medium text-gray-900'
>
{
t
(
'common.members.invitationLink'
)
}
</
div
>
<
InvitationLink
value=
{
invitationLink
}
/>
</
div
>
<
div
className=
'flex justify-end'
>
<
div
className=
'flex justify-end'
>
<
Button
<
Button
className=
'w-[96px] text-sm font-medium'
className=
'w-[96px] text-sm font-medium'
onClick=
{
onCancel
}
onClick=
{
onCancel
}
type=
'primary'
type=
'primary'
>
>
...
@@ -42,4 +48,4 @@ const InvitedModal = ({
...
@@ -42,4 +48,4 @@ const InvitedModal = ({
)
)
}
}
export
default
InvitedModal
export
default
InvitedModal
\ No newline at end of file
web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx
0 → 100644
View file @
477f1b37
'use client'
import
React
,
{
useEffect
,
useState
}
from
'react'
import
copy
from
'copy-to-clipboard'
import
{
t
}
from
'i18next'
import
s
from
'./index.module.css'
import
Tooltip
from
'@/app/components/base/tooltip'
type
IInvitationLinkProps
=
{
value
?:
string
}
const
InvitationLink
=
({
value
=
''
,
}:
IInvitationLinkProps
)
=>
{
const
[
isCopied
,
setIsCopied
]
=
useState
(
false
)
useEffect
(()
=>
{
if
(
isCopied
)
{
const
timeout
=
setTimeout
(()
=>
{
setIsCopied
(
false
)
},
1000
)
return
()
=>
{
clearTimeout
(
timeout
)
}
}
},
[
isCopied
])
return
(
<
div
className=
'flex rounded-lg bg-gray-100 hover:bg-gray-100 border border-gray-200 py-2 items-center'
>
<
div
className=
"flex items-center flex-grow h-5"
>
<
div
className=
'flex-grow bg-gray-100 text-[13px] relative h-full'
>
<
Tooltip
selector=
"top-uniq"
content=
{
isCopied
?
`${t('appApi.copied')}`
:
`${t('appApi.copy')}`
}
className=
'z-10'
>
<
div
className=
'absolute top-0 left-0 w-full pl-2 pr-2 truncate cursor-pointer r-0'
onClick=
{
()
=>
{
copy
(
value
)
setIsCopied
(
true
)
}
}
>
{
value
}
</
div
>
</
Tooltip
>
</
div
>
<
div
className=
"flex-shrink-0 h-4 bg-gray-200 border"
/>
<
Tooltip
selector=
"top-uniq"
content=
{
isCopied
?
`${t('appApi.copied')}`
:
`${t('appApi.copy')}`
}
className=
'z-10'
>
<
div
className=
"px-0.5 flex-shrink-0"
>
<
div
className=
{
`box-border w-[30px] h-[30px] flex items-center justify-center rounded-lg hover:bg-gray-100 cursor-pointer ${s.copyIcon} ${isCopied ? s.copied : ''}`
}
onClick=
{
()
=>
{
copy
(
value
)
setIsCopied
(
true
)
}
}
>
</
div
>
</
div
>
</
Tooltip
>
</
div
>
</
div
>
)
}
export
default
InvitationLink
web/app/install/installForm.tsx
View file @
477f1b37
'use client'
'use client'
import
React
from
'react'
import
React
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
Button
from
'@/app/components/base/button'
import
Link
from
'next/link'
import
Link
from
'next/link'
import
{
useRouter
}
from
'next/navigation'
import
{
useRouter
}
from
'next/navigation'
import
Toast
from
'../components/base/toast'
import
Toast
from
'../components/base/toast'
import
Button
from
'@/app/components/base/button'
import
{
setup
}
from
'@/service/common'
import
{
setup
}
from
'@/service/common'
const
validEmailReg
=
/^
[\w\.
-
]
+@
([\w
-
]
+
\.)
+
[\w
-
]{2,}
$/
const
validEmailReg
=
/^
[\w\.
-
]
+@
([\w
-
]
+
\.)
+
[\w
-
]{2,}
$/
...
@@ -40,36 +40,37 @@ const InstallForm = () => {
...
@@ -40,36 +40,37 @@ const InstallForm = () => {
showErrorMessage
(
t
(
'login.error.passwordEmpty'
))
showErrorMessage
(
t
(
'login.error.passwordEmpty'
))
return
false
return
false
}
}
if
(
!
validPassword
.
test
(
password
))
{
if
(
!
validPassword
.
test
(
password
))
showErrorMessage
(
t
(
'login.error.passwordInvalid'
))
showErrorMessage
(
t
(
'login.error.passwordInvalid'
))
}
return
true
return
true
}
}
const
handleSetting
=
async
()
=>
{
const
handleSetting
=
async
()
=>
{
if
(
!
valid
())
return
if
(
!
valid
())
return
await
setup
({
await
setup
({
body
:
{
body
:
{
email
,
email
,
name
,
name
,
password
password
,
}
}
,
})
})
router
.
push
(
'/signin'
)
router
.
push
(
'/signin'
)
}
}
return
(
return
(
<>
<>
<
div
className=
"sm:mx-auto sm:w-full sm:max-w-md"
>
<
div
className=
"sm:mx-auto sm:w-full sm:max-w-md"
>
<
h2
className=
"text-
3xl font-normal
text-gray-900"
>
{
t
(
'login.setAdminAccount'
)
}
</
h2
>
<
h2
className=
"text-
[32px] font-bold
text-gray-900"
>
{
t
(
'login.setAdminAccount'
)
}
</
h2
>
<
p
className=
'
<
p
className=
'
mt-
2
text-sm text-gray-600
mt-
1
text-sm text-gray-600
'
>
{
t
(
'login.setAdminAccountDesc'
)
}
</
p
>
'
>
{
t
(
'login.setAdminAccountDesc'
)
}
</
p
>
</
div
>
</
div
>
<
div
className=
"grow mt-8 sm:mx-auto sm:w-full sm:max-w-md"
>
<
div
className=
"grow mt-8 sm:mx-auto sm:w-full sm:max-w-md"
>
<
div
className=
"bg-white "
>
<
div
className=
"bg-white "
>
<
form
className=
"space-y-6"
onSubmit=
{
()
=>
{
}
}
>
<
form
onSubmit=
{
()
=>
{
}
}
>
<
div
>
<
div
className=
'mb-5'
>
<
label
htmlFor=
"email"
className=
"
block text-sm font-medium text-gray-7
00"
>
<
label
htmlFor=
"email"
className=
"
my-2 flex items-center justify-between text-sm font-medium text-gray-9
00"
>
{
t
(
'login.email'
)
}
{
t
(
'login.email'
)
}
</
label
>
</
label
>
<
div
className=
"mt-1"
>
<
div
className=
"mt-1"
>
...
@@ -78,13 +79,14 @@ const InstallForm = () => {
...
@@ -78,13 +79,14 @@ const InstallForm = () => {
type=
"email"
type=
"email"
value=
{
email
}
value=
{
email
}
onChange=
{
e
=>
setEmail
(
e
.
target
.
value
)
}
onChange=
{
e
=>
setEmail
(
e
.
target
.
value
)
}
className=
{
'appearance-none block w-full px-3 py-2 border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 rounded-md shadow-sm placeholder-gray-400 sm:text-sm'
}
placeholder=
{
t
(
'login.emailPlaceholder'
)
||
''
}
className=
{
'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm'
}
/>
/>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
>
<
div
className=
'mb-5'
>
<
label
htmlFor=
"name"
className=
"
block text-sm font-medium text-gray-7
00"
>
<
label
htmlFor=
"name"
className=
"
my-2 flex items-center justify-between text-sm font-medium text-gray-9
00"
>
{
t
(
'login.name'
)
}
{
t
(
'login.name'
)
}
</
label
>
</
label
>
<
div
className=
"mt-1 relative rounded-md shadow-sm"
>
<
div
className=
"mt-1 relative rounded-md shadow-sm"
>
...
@@ -93,13 +95,14 @@ const InstallForm = () => {
...
@@ -93,13 +95,14 @@ const InstallForm = () => {
type=
"text"
type=
"text"
value=
{
name
}
value=
{
name
}
onChange=
{
e
=>
setName
(
e
.
target
.
value
)
}
onChange=
{
e
=>
setName
(
e
.
target
.
value
)
}
className=
{
'appearance-none block w-full px-3 py-2 border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 rounded-md shadow-sm placeholder-gray-400 sm:text-sm pr-10'
}
placeholder=
{
t
(
'login.namePlaceholder'
)
||
''
}
className=
{
'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm pr-10'
}
/>
/>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
>
<
label
htmlFor=
"password"
className=
"block text-sm font-medium text-gray-700"
>
<
div
className=
'mb-5'
>
<
label
htmlFor=
"password"
className=
"my-2 flex items-center justify-between text-sm font-medium text-gray-900"
>
{
t
(
'login.password'
)
}
{
t
(
'login.password'
)
}
</
label
>
</
label
>
<
div
className=
"mt-1 relative rounded-md shadow-sm"
>
<
div
className=
"mt-1 relative rounded-md shadow-sm"
>
...
@@ -108,7 +111,8 @@ const InstallForm = () => {
...
@@ -108,7 +111,8 @@ const InstallForm = () => {
type=
'password'
type=
'password'
value=
{
password
}
value=
{
password
}
onChange=
{
e
=>
setPassword
(
e
.
target
.
value
)
}
onChange=
{
e
=>
setPassword
(
e
.
target
.
value
)
}
className=
{
'appearance-none block w-full px-3 py-2 border border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 rounded-md shadow-sm placeholder-gray-400 sm:text-sm pr-10'
}
placeholder=
{
t
(
'login.passwordPlaceholder'
)
||
''
}
className=
{
'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm pr-10'
}
/>
/>
</
div
>
</
div
>
<
div
className=
'mt-1 text-xs text-gray-500'
>
{
t
(
'login.error.passwordInvalid'
)
}
</
div
>
<
div
className=
'mt-1 text-xs text-gray-500'
>
{
t
(
'login.error.passwordInvalid'
)
}
</
div
>
...
@@ -123,29 +127,21 @@ const InstallForm = () => {
...
@@ -123,29 +127,21 @@ const InstallForm = () => {
</div>
</div>
</div>
</div>
</div> */
}
</div> */
}
{
/* agree to our Terms and Privacy Policy. */
}
<
div
className=
"block mt-6 text-xs text-gray-600"
>
{
t
(
'login.tosDesc'
)
}
<
Link
className=
'text-primary-600'
target=
{
'_blank'
}
href=
'https://docs.dify.ai/user-agreement/terms-of-service'
>
{
t
(
'login.tos'
)
}
</
Link
>
&
<
Link
className=
'text-primary-600'
target=
{
'_blank'
}
href=
'https://langgenius.ai/privacy-policy'
>
{
t
(
'login.pp'
)
}
</
Link
>
</
div
>
<
div
>
<
div
>
<
Button
type=
'primary'
onClick=
{
handleSetting
}
>
<
Button
type=
'primary'
className=
'w-full !fone-medium !text-sm'
onClick=
{
handleSetting
}
>
{
t
(
'login.installBtn'
)
}
{
t
(
'login.installBtn'
)
}
</
Button
>
</
Button
>
</
div
>
</
div
>
</
form
>
</
form
>
<
div
className=
"block w-hull mt-2 text-xs text-gray-600"
>
{
t
(
'login.license.tip'
)
}
<
Link
className=
'text-primary-600'
target=
{
'_blank'
}
href=
'https://docs.dify.ai/community/open-source'
>
{
t
(
'login.license.link'
)
}
</
Link
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</>
</>
...
...
web/app/signin/_header.tsx
View file @
477f1b37
'use client'
'use client'
import
React
from
'react'
import
React
from
'react'
import
{
useContext
}
from
'use-context-selector'
import
style
from
'./page.module.css'
import
style
from
'./page.module.css'
import
Select
,
{
LOCALES
}
from
'@/app/components/base/select/locale'
import
Select
,
{
LOCALES
}
from
'@/app/components/base/select/locale'
import
{
type
Locale
}
from
'@/i18n'
import
{
type
Locale
}
from
'@/i18n'
import
I18n
from
'@/context/i18n'
import
I18n
from
'@/context/i18n'
import
{
setLocaleOnClient
}
from
'@/i18n/client'
import
{
useContext
}
from
'use-context-selector'
type
IHeaderProps
=
{
locale
:
string
}
const
Header
=
()
=>
{
const
Header
=
()
=>
{
const
{
locale
,
setLocaleOnClient
}
=
useContext
(
I18n
)
const
{
locale
,
setLocaleOnClient
}
=
useContext
(
I18n
)
...
...
web/app/signin/forms.tsx
View file @
477f1b37
...
@@ -2,9 +2,9 @@
...
@@ -2,9 +2,9 @@
import
React
from
'react'
import
React
from
'react'
import
{
useSearchParams
}
from
'next/navigation'
import
{
useSearchParams
}
from
'next/navigation'
import
cn
from
'classnames'
import
NormalForm
from
'./normalForm'
import
NormalForm
from
'./normalForm'
import
OneMoreStep
from
'./oneMoreStep'
import
OneMoreStep
from
'./oneMoreStep'
import
classNames
from
'classnames'
const
Forms
=
()
=>
{
const
Forms
=
()
=>
{
const
searchParams
=
useSearchParams
()
const
searchParams
=
useSearchParams
()
...
@@ -19,7 +19,7 @@ const Forms = () => {
...
@@ -19,7 +19,7 @@ const Forms = () => {
}
}
}
}
return
<
div
className=
{
return
<
div
className=
{
c
lassNames
(
c
n
(
'flex flex-col items-center w-full grow items-center justify-center'
,
'flex flex-col items-center w-full grow items-center justify-center'
,
'px-6'
,
'px-6'
,
'md:px-[108px]'
,
'md:px-[108px]'
,
...
@@ -28,7 +28,6 @@ const Forms = () => {
...
@@ -28,7 +28,6 @@ const Forms = () => {
<
div
className=
'flex flex-col md:w-[400px]'
>
<
div
className=
'flex flex-col md:w-[400px]'
>
{
getForm
()
}
{
getForm
()
}
</
div
>
</
div
>
</
div
>
</
div
>
}
}
...
...
web/app/signin/normalForm.tsx
View file @
477f1b37
...
@@ -2,16 +2,15 @@
...
@@ -2,16 +2,15 @@
import
React
,
{
useEffect
,
useReducer
,
useState
}
from
'react'
import
React
,
{
useEffect
,
useReducer
,
useState
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
{
useRouter
}
from
'next/navigation'
import
{
useRouter
}
from
'next/navigation'
import
{
IS_CE_EDITION
}
from
'@/config'
import
classNames
from
'classnames'
import
classNames
from
'classnames'
import
useSWR
from
'swr'
import
useSWR
from
'swr'
import
Link
from
'next/link'
import
Link
from
'next/link'
import
Toast
from
'../components/base/toast'
import
style
from
'./page.module.css'
import
style
from
'./page.module.css'
// import Tooltip from '@/app/components/base/tooltip/index'
// import Tooltip from '@/app/components/base/tooltip/index'
import
Toast
from
'../components/base/toast
'
import
{
IS_CE_EDITION
,
apiPrefix
}
from
'@/config
'
import
Button
from
'@/app/components/base/button'
import
Button
from
'@/app/components/base/button'
import
{
login
,
oauth
}
from
'@/service/common'
import
{
login
,
oauth
}
from
'@/service/common'
import
{
apiPrefix
}
from
'@/config'
const
validEmailReg
=
/^
[\w\.
-
]
+@
([\w
-
]
+
\.)
+
[\w
-
]{2,}
$/
const
validEmailReg
=
/^
[\w\.
-
]
+@
([\w
-
]
+
\.)
+
[\w
-
]{2,}
$/
...
@@ -92,7 +91,8 @@ const NormalForm = () => {
...
@@ -92,7 +91,8 @@ const NormalForm = () => {
},
},
})
})
router
.
push
(
'/'
)
router
.
push
(
'/'
)
}
finally
{
}
finally
{
setIsLoading
(
false
)
setIsLoading
(
false
)
}
}
}
}
...
@@ -132,8 +132,8 @@ const NormalForm = () => {
...
@@ -132,8 +132,8 @@ const NormalForm = () => {
return
(
return
(
<>
<>
<
div
className=
"w-full mx-auto"
>
<
div
className=
"w-full mx-auto"
>
<
h2
className=
"text-
3xl font-normal
text-gray-900"
>
{
t
(
'login.pageTitle'
)
}
</
h2
>
<
h2
className=
"text-
[32px] font-bold
text-gray-900"
>
{
t
(
'login.pageTitle'
)
}
</
h2
>
<
p
className=
'mt-
2 text-sm text-gray-600
'
>
{
t
(
'login.welcome'
)
}
</
p
>
<
p
className=
'mt-
1 text-sm text-gray-600
'
>
{
t
(
'login.welcome'
)
}
</
p
>
</
div
>
</
div
>
<
div
className=
"w-full mx-auto mt-8"
>
<
div
className=
"w-full mx-auto mt-8"
>
...
@@ -145,7 +145,7 @@ const NormalForm = () => {
...
@@ -145,7 +145,7 @@ const NormalForm = () => {
<
Button
<
Button
type=
'default'
type=
'default'
disabled=
{
isLoading
}
disabled=
{
isLoading
}
className=
'w-full'
className=
'w-full
hover:!bg-gray-50 !text-sm !font-medium
'
>
>
<>
<>
<
span
className=
{
<
span
className=
{
...
@@ -154,7 +154,7 @@ const NormalForm = () => {
...
@@ -154,7 +154,7 @@ const NormalForm = () => {
'w-5 h-5 mr-2'
,
'w-5 h-5 mr-2'
,
)
)
}
/>
}
/>
<
span
className=
"truncate"
>
{
t
(
'login.withGitHub'
)
}
</
span
>
<
span
className=
"truncate
text-gray-800
"
>
{
t
(
'login.withGitHub'
)
}
</
span
>
</>
</>
</
Button
>
</
Button
>
</
a
>
</
a
>
...
@@ -164,7 +164,7 @@ const NormalForm = () => {
...
@@ -164,7 +164,7 @@ const NormalForm = () => {
<
Button
<
Button
type=
'default'
type=
'default'
disabled=
{
isLoading
}
disabled=
{
isLoading
}
className=
'w-full'
className=
'w-full
hover:!bg-gray-50 !text-sm !font-medium
'
>
>
<>
<>
<
span
className=
{
<
span
className=
{
...
@@ -173,7 +173,7 @@ const NormalForm = () => {
...
@@ -173,7 +173,7 @@ const NormalForm = () => {
'w-5 h-5 mr-2'
,
'w-5 h-5 mr-2'
,
)
)
}
/>
}
/>
<
span
className=
"truncate"
>
{
t
(
'login.withGoogle'
)
}
</
span
>
<
span
className=
"truncate
text-gray-800
"
>
{
t
(
'login.withGoogle'
)
}
</
span
>
</>
</>
</
Button
>
</
Button
>
</
a
>
</
a
>
...
@@ -192,9 +192,9 @@ const NormalForm = () => {
...
@@ -192,9 +192,9 @@ const NormalForm = () => {
</div>
</div>
</div> */
}
</div> */
}
<
form
className=
"space-y-6"
onSubmit=
{
()
=>
{
}
}
>
<
form
onSubmit=
{
()
=>
{
}
}
>
<
div
>
<
div
className=
'mb-5'
>
<
label
htmlFor=
"email"
className=
"
block text-sm font-medium text-gray-7
00"
>
<
label
htmlFor=
"email"
className=
"
my-2 block text-sm font-medium text-gray-9
00"
>
{
t
(
'login.email'
)
}
{
t
(
'login.email'
)
}
</
label
>
</
label
>
<
div
className=
"mt-1"
>
<
div
className=
"mt-1"
>
...
@@ -204,13 +204,14 @@ const NormalForm = () => {
...
@@ -204,13 +204,14 @@ const NormalForm = () => {
id=
"email"
id=
"email"
type=
"email"
type=
"email"
autoComplete=
"email"
autoComplete=
"email"
className=
{
'appearance-none block w-full px-3 py-2 border border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 rounded-md shadow-sm placeholder-gray-400 sm:text-sm'
}
placeholder=
{
t
(
'login.emailPlaceholder'
)
||
''
}
className=
{
'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm'
}
/>
/>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
>
<
div
className=
'mb-4'
>
<
label
htmlFor=
"password"
className=
"
flex items-center justify-between text-sm font-medium text-gray-7
00"
>
<
label
htmlFor=
"password"
className=
"
my-2 flex items-center justify-between text-sm font-medium text-gray-9
00"
>
<
span
>
{
t
(
'login.password'
)
}
</
span
>
<
span
>
{
t
(
'login.password'
)
}
</
span
>
{
/* <Tooltip
{
/* <Tooltip
selector='forget-password'
selector='forget-password'
...
@@ -235,10 +236,8 @@ const NormalForm = () => {
...
@@ -235,10 +236,8 @@ const NormalForm = () => {
onChange=
{
e
=>
setPassword
(
e
.
target
.
value
)
}
onChange=
{
e
=>
setPassword
(
e
.
target
.
value
)
}
type=
{
showPassword
?
'text'
:
'password'
}
type=
{
showPassword
?
'text'
:
'password'
}
autoComplete=
"current-password"
autoComplete=
"current-password"
className=
{
`appearance-none block w-full px-3 py-2
placeholder=
{
t
(
'login.passwordPlaceholder'
)
||
''
}
border border-gray-300
className=
{
'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm pr-10'
}
focus:outline-none focus:ring-indigo-500 focus:border-indigo-500
rounded-md shadow-sm placeholder-gray-400 sm:text-sm pr-10`
}
/>
/>
<
div
className=
"absolute inset-y-0 right-0 flex items-center pr-3"
>
<
div
className=
"absolute inset-y-0 right-0 flex items-center pr-3"
>
<
button
<
button
...
@@ -252,18 +251,19 @@ const NormalForm = () => {
...
@@ -252,18 +251,19 @@ const NormalForm = () => {
</
div
>
</
div
>
</
div
>
</
div
>
<
div
>
<
div
className=
'mb-2'
>
<
Button
<
Button
type=
'primary'
type=
'primary'
onClick=
{
handleEmailPasswordLogin
}
onClick=
{
handleEmailPasswordLogin
}
disabled=
{
isLoading
}
disabled=
{
isLoading
}
className=
"w-full !fone-medium !text-sm"
>
{
t
(
'login.signBtn'
)
}
</
Button
>
>
{
t
(
'login.signBtn'
)
}
</
Button
>
</
div
>
</
div
>
</
form
>
</
form
>
</>
</>
}
}
{
/* agree to our Terms and Privacy Policy. */
}
{
/* agree to our Terms and Privacy Policy. */
}
<
div
className=
"
block mt-6
text-xs text-gray-600"
>
<
div
className=
"
w-hull text-center block mt-2
text-xs text-gray-600"
>
{
t
(
'login.tosDesc'
)
}
{
t
(
'login.tosDesc'
)
}
<
Link
<
Link
...
...
web/app/signin/oneMoreStep.tsx
View file @
477f1b37
'use client'
'use client'
import
React
,
{
useEffect
,
useReducer
}
from
'react'
import
React
,
{
useEffect
,
useReducer
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
Link
from
'next/link'
import
useSWR
from
'swr'
import
useSWR
from
'swr'
import
{
useRouter
}
from
'next/navigation'
import
{
useRouter
}
from
'next/navigation'
import
Button
from
'@/app/components/base/button'
import
Button
from
'@/app/components/base/button'
...
@@ -74,14 +75,14 @@ const OneMoreStep = () => {
...
@@ -74,14 +75,14 @@ const OneMoreStep = () => {
return
(
return
(
<>
<>
<
div
className=
"w-full mx-auto"
>
<
div
className=
"w-full mx-auto"
>
<
h2
className=
"text-
3xl font-normal
text-gray-900"
>
{
t
(
'login.oneMoreStep'
)
}
</
h2
>
<
h2
className=
"text-
[32px] font-bold
text-gray-900"
>
{
t
(
'login.oneMoreStep'
)
}
</
h2
>
<
p
className=
'mt-
2
text-sm text-gray-600 '
>
{
t
(
'login.createSample'
)
}
</
p
>
<
p
className=
'mt-
1
text-sm text-gray-600 '
>
{
t
(
'login.createSample'
)
}
</
p
>
</
div
>
</
div
>
<
div
className=
"w-full mx-auto mt-
8
"
>
<
div
className=
"w-full mx-auto mt-
6
"
>
<
div
className=
"
space-y-6
bg-white"
>
<
div
className=
"bg-white"
>
<
div
className=
""
>
<
div
className=
"
mb-5
"
>
<
label
className=
"flex items-center justify-between text-sm font-medium text-gray-900"
>
<
label
className=
"
my-2
flex items-center justify-between text-sm font-medium text-gray-900"
>
{
t
(
'login.invitationCode'
)
}
{
t
(
'login.invitationCode'
)
}
<
Tooltip
<
Tooltip
clickable
clickable
...
@@ -103,16 +104,16 @@ const OneMoreStep = () => {
...
@@ -103,16 +104,16 @@ const OneMoreStep = () => {
id=
"invitation_code"
id=
"invitation_code"
value=
{
state
.
invitation_code
}
value=
{
state
.
invitation_code
}
type=
"text"
type=
"text"
className=
{
'appearance-none block w-full px-3 py-2 border border-gray-300 focus:outline-none focus:ring-primary-600 focus:border-primary-600 rounded-md shadow-sm placeholder-gray-400 sm:text-sm'
}
placeholder=
{
t
(
'login.invitationCodePlaceholder'
)
||
''
}
className=
{
'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm'
}
onChange=
{
(
e
)
=>
{
onChange=
{
(
e
)
=>
{
dispatch
({
type
:
'invitation_code'
,
value
:
e
.
target
.
value
.
trim
()
})
dispatch
({
type
:
'invitation_code'
,
value
:
e
.
target
.
value
.
trim
()
})
}
}
}
}
/>
/>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
className=
'mb-5'
>
<
div
>
<
label
htmlFor=
"name"
className=
"my-2 flex items-center justify-between text-sm font-medium text-gray-900"
>
<
label
htmlFor=
"name"
className=
"block text-sm font-medium text-gray-700"
>
{
t
(
'login.interfaceLanguage'
)
}
{
t
(
'login.interfaceLanguage'
)
}
</
label
>
</
label
>
<
div
className=
"relative mt-1 rounded-md shadow-sm"
>
<
div
className=
"relative mt-1 rounded-md shadow-sm"
>
...
@@ -125,8 +126,7 @@ const OneMoreStep = () => {
...
@@ -125,8 +126,7 @@ const OneMoreStep = () => {
/>
/>
</
div
>
</
div
>
</
div
>
</
div
>
<
div
>
<
div
className=
'mb-4'
>
<
label
htmlFor=
"timezone"
className=
"block text-sm font-medium text-gray-700"
>
<
label
htmlFor=
"timezone"
className=
"block text-sm font-medium text-gray-700"
>
{
t
(
'login.timezone'
)
}
{
t
(
'login.timezone'
)
}
</
label
>
</
label
>
...
@@ -143,6 +143,7 @@ const OneMoreStep = () => {
...
@@ -143,6 +143,7 @@ const OneMoreStep = () => {
<
div
>
<
div
>
<
Button
<
Button
type=
'primary'
type=
'primary'
className=
'w-full !fone-medium !text-sm'
disabled=
{
state
.
formState
===
'processing'
}
disabled=
{
state
.
formState
===
'processing'
}
onClick=
{
()
=>
{
onClick=
{
()
=>
{
dispatch
({
type
:
'formState'
,
value
:
'processing'
})
dispatch
({
type
:
'formState'
,
value
:
'processing'
})
...
@@ -151,6 +152,15 @@ const OneMoreStep = () => {
...
@@ -151,6 +152,15 @@ const OneMoreStep = () => {
{
t
(
'login.go'
)
}
{
t
(
'login.go'
)
}
</
Button
>
</
Button
>
</
div
>
</
div
>
<
div
className=
"block w-hull mt-2 text-xs text-gray-600"
>
{
t
(
'login.license.tip'
)
}
<
Link
className=
'text-primary-600'
target=
{
'_blank'
}
href=
'https://docs.dify.ai/community/open-source'
>
{
t
(
'login.license.link'
)
}
</
Link
>
</
div
>
</
div
>
</
div
>
</
div
>
</
div
>
</>
</>
...
...
web/app/signin/page.tsx
View file @
477f1b37
import
React
from
'react'
import
React
from
'react'
import
cn
from
'classnames'
import
Forms
from
'./forms'
import
Forms
from
'./forms'
import
Header
from
'./_header'
import
Header
from
'./_header'
import
style
from
'./page.module.css'
import
style
from
'./page.module.css'
import
classNames
from
'classnames'
const
SignIn
=
()
=>
{
const
SignIn
=
()
=>
{
return
(
return
(
<>
<>
<
div
className=
{
c
lassNames
(
<
div
className=
{
c
n
(
style
.
background
,
style
.
background
,
'flex w-full min-h-screen'
,
'flex w-full min-h-screen'
,
'sm:p-4 lg:p-8'
,
'sm:p-4 lg:p-8'
,
'gap-x-20'
,
'gap-x-20'
,
'justify-center lg:justify-start'
'justify-center lg:justify-start'
,
)
}
>
)
}
>
<
div
className=
{
<
div
className=
{
c
lassNames
(
c
n
(
'flex w-full flex-col bg-white shadow rounded-2xl shrink-0'
,
'flex w-full flex-col bg-white shadow rounded-2xl shrink-0'
,
'space-between'
'space-between'
,
)
)
}
>
}
>
<
Header
/>
<
Header
/>
...
...
web/i18n/lang/common.en.ts
View file @
477f1b37
...
@@ -109,15 +109,16 @@ const translation = {
...
@@ -109,15 +109,16 @@ const translation = {
admin
:
'Admin'
,
admin
:
'Admin'
,
adminTip
:
'Can build apps & manage team settings'
,
adminTip
:
'Can build apps & manage team settings'
,
normal
:
'Normal'
,
normal
:
'Normal'
,
normalTip
:
'Only can use apps
,
can not build apps'
,
normalTip
:
'Only can use apps
,
can not build apps'
,
inviteTeamMember
:
'Add team member'
,
inviteTeamMember
:
'Add team member'
,
inviteTeamMemberTip
:
'They can access your team data directly after signing in.'
,
inviteTeamMemberTip
:
'They can access your team data directly after signing in.'
,
email
:
'Email'
,
email
:
'Email'
,
emailInvalid
:
'Invalid Email Format'
,
emailInvalid
:
'Invalid Email Format'
,
emailPlaceholder
:
'Input Email'
,
emailPlaceholder
:
'Input Email'
,
sendInvite
:
'Add'
,
sendInvite
:
'Add'
,
invitationSent
:
'Added'
,
invitationSent
:
'Invitation sent'
,
invitationSentTip
:
'Added, and they can sign in to Dify to access your team data.'
,
invitationSentTip
:
'Invitation sent, and they can sign in to Dify to access your team data.'
,
invitationLink
:
'Invitation Link'
,
ok
:
'OK'
,
ok
:
'OK'
,
removeFromTeam
:
'Remove from team'
,
removeFromTeam
:
'Remove from team'
,
removeFromTeamTip
:
'Will remove team access'
,
removeFromTeamTip
:
'Will remove team access'
,
...
...
web/i18n/lang/common.zh.ts
View file @
477f1b37
...
@@ -117,8 +117,9 @@ const translation = {
...
@@ -117,8 +117,9 @@ const translation = {
emailInvalid
:
'邮箱格式无效'
,
emailInvalid
:
'邮箱格式无效'
,
emailPlaceholder
:
'输入邮箱'
,
emailPlaceholder
:
'输入邮箱'
,
sendInvite
:
'添加'
,
sendInvite
:
'添加'
,
invitationSent
:
'已添加'
,
invitationSent
:
'邀请已发送'
,
invitationSentTip
:
'已添加,对方登录 Dify 后即可访问你的团队数据。'
,
invitationSentTip
:
'邀请已发送,对方登录 Dify 后即可访问你的团队数据。'
,
invitationLink
:
'邀请链接'
,
ok
:
'好的'
,
ok
:
'好的'
,
removeFromTeam
:
'移除团队'
,
removeFromTeam
:
'移除团队'
,
removeFromTeamTip
:
'将取消团队访问'
,
removeFromTeamTip
:
'将取消团队访问'
,
...
...
web/i18n/lang/login.en.ts
View file @
477f1b37
const
translation
=
{
const
translation
=
{
"pageTitle"
:
"Hey, let's get started!👋"
,
pageTitle
:
'Hey, let
\'
s get started!👋'
,
"welcome"
:
"Welcome to Dify, please log in to continue."
,
welcome
:
'Welcome to Dify, please log in to continue.'
,
"email"
:
"Email address"
,
email
:
'Email address'
,
"password"
:
"Password"
,
emailPlaceholder
:
'Your email'
,
"name"
:
"Name"
,
password
:
'Password'
,
"forget"
:
"Forgot your password?"
,
passwordPlaceholder
:
'Your password'
,
"signBtn"
:
"Sign in"
,
name
:
'Username'
,
"installBtn"
:
"Setting"
,
namePlaceholder
:
'Your username'
,
"setAdminAccount"
:
"Setting up an admin account"
,
forget
:
'Forgot your password?'
,
"setAdminAccountDesc"
:
"Maximum privileges for admin account, which can be used to create applications and manage LLM providers, etc."
,
signBtn
:
'Sign in'
,
"createAndSignIn"
:
"Create and sign in"
,
installBtn
:
'Setting'
,
"oneMoreStep"
:
"One more step"
,
setAdminAccount
:
'Setting up an admin account'
,
"createSample"
:
"Based on this information, we’ll create sample application for you"
,
setAdminAccountDesc
:
'Maximum privileges for admin account, which can be used to create applications and manage LLM providers, etc.'
,
"invitationCode"
:
"Invitation Code"
,
createAndSignIn
:
'Create and sign in'
,
"interfaceLanguage"
:
"Interface Dify"
,
oneMoreStep
:
'One more step'
,
"timezone"
:
"Time zone"
,
createSample
:
'Based on this information, we’ll create sample application for you'
,
"go"
:
"Go to Dify"
,
invitationCode
:
'Invitation Code'
,
"sendUsMail"
:
"Email us your introduction, and we'll handle the invitation request."
,
invitationCodePlaceholder
:
'Your invitation code'
,
"acceptPP"
:
"I have read and accept the privacy policy"
,
interfaceLanguage
:
'Interface Language'
,
"reset"
:
"Please run following command to reset your password"
,
timezone
:
'Time zone'
,
"withGitHub"
:
"Continue with GitHub"
,
go
:
'Go to Dify'
,
"withGoogle"
:
"Continue with Google"
,
sendUsMail
:
'Email us your introduction, and we
\'
ll handle the invitation request.'
,
"rightTitle"
:
"Unlock the full potential of LLM"
,
acceptPP
:
'I have read and accept the privacy policy'
,
"rightDesc"
:
"Effortlessly build visually captivating, operable, and improvable AI applications."
,
reset
:
'Please run following command to reset your password'
,
"tos"
:
"Terms of Service"
,
withGitHub
:
'Continue with GitHub'
,
"pp"
:
"Privacy Policy"
,
withGoogle
:
'Continue with Google'
,
"tosDesc"
:
"By signing up, you agree to our"
,
rightTitle
:
'Unlock the full potential of LLM'
,
"donthave"
:
"Don't have?"
,
rightDesc
:
'Effortlessly build visually captivating, operable, and improvable AI applications.'
,
"invalidInvitationCode"
:
"Invalid invitation code"
,
tos
:
'Terms of Service'
,
"accountAlreadyInited"
:
"Account already inited"
,
pp
:
'Privacy Policy'
,
"error"
:
{
tosDesc
:
'By signing up, you agree to our'
,
"emailEmpty"
:
"Email address is required"
,
donthave
:
'Don
\'
t have?'
,
"emailInValid"
:
"Please enter a valid email address"
,
invalidInvitationCode
:
'Invalid invitation code'
,
"nameEmpty"
:
"Name is required"
,
accountAlreadyInited
:
'Account already inited'
,
"passwordEmpty"
:
"Password is required"
,
error
:
{
"passwordInvalid"
:
"Password must contain letters and numbers, and the length must be greater than 8"
,
emailEmpty
:
'Email address is required'
,
}
emailInValid
:
'Please enter a valid email address'
,
nameEmpty
:
'Name is required'
,
passwordEmpty
:
'Password is required'
,
passwordInvalid
:
'Password must contain letters and numbers, and the length must be greater than 8'
,
},
license
:
{
tip
:
'Before starting Dify Community Edition, read the GitHub'
,
link
:
'Open-source License'
,
},
join
:
'Join'
,
joinTipStart
:
'Invite you join'
,
joinTipEnd
:
'team on Dify'
,
invalid
:
'The link has expired'
,
explore
:
'Explore Dify'
,
activatedTipStart
:
'You have joined the'
,
activatedTipEnd
:
'team'
,
activated
:
'Sign In Now'
,
}
}
export
default
translation
export
default
translation
web/i18n/lang/login.zh.ts
View file @
477f1b37
const
translation
=
{
const
translation
=
{
"pageTitle"
:
"嗨,近来可好 👋"
,
pageTitle
:
'嗨,近来可好 👋'
,
"welcome"
:
"欢迎来到 Dify, 登录以继续"
,
welcome
:
'欢迎来到 Dify, 登录以继续'
,
"email"
:
"邮箱"
,
email
:
'邮箱'
,
"password"
:
"密码"
,
emailPlaceholder
:
'输入邮箱地址'
,
"name"
:
"用户名"
,
password
:
'密码'
,
"forget"
:
"忘记密码?"
,
passwordPlaceholder
:
'输入密码'
,
"signBtn"
:
"登录"
,
name
:
'用户名'
,
"installBtn"
:
"设置"
,
namePlaceholder
:
'输入用户名'
,
"setAdminAccount"
:
"设置管理员账户"
,
forget
:
'忘记密码?'
,
"setAdminAccountDesc"
:
"管理员拥有的最大权限,可用于创建应用和管理 LLM 供应商等。"
,
signBtn
:
'登录'
,
"createAndSignIn"
:
"创建账户"
,
installBtn
:
'设置'
,
"oneMoreStep"
:
"还差一步"
,
setAdminAccount
:
'设置管理员账户'
,
"createSample"
:
"基于这些信息,我们将为您创建一个示例应用"
,
setAdminAccountDesc
:
'管理员拥有的最大权限,可用于创建应用和管理 LLM 供应商等。'
,
"invitationCode"
:
"邀请码"
,
createAndSignIn
:
'创建账户'
,
"interfaceLanguage"
:
"界面语言"
,
oneMoreStep
:
'还差一步'
,
"timezone"
:
"时区"
,
createSample
:
'基于这些信息,我们将为您创建一个示例应用'
,
"go"
:
"跳转至 Dify"
,
invitationCode
:
'邀请码'
,
"sendUsMail"
:
"发封邮件介绍你自己,我们会尽快处理。"
,
invitationCodePlaceholder
:
'输入邀请码'
,
"acceptPP"
:
"我已阅读并接受隐私政策"
,
interfaceLanguage
:
'界面语言'
,
"reset"
:
"请运行以下命令重置密码"
,
timezone
:
'时区'
,
"withGitHub"
:
"使用 GitHub 登录"
,
go
:
'跳转至 Dify'
,
"withGoogle"
:
"使用 Google 登录"
,
sendUsMail
:
'发封邮件介绍你自己,我们会尽快处理。'
,
"rightTitle"
:
"释放大型语言模型的全部潜能"
,
acceptPP
:
'我已阅读并接受隐私政策'
,
"rightDesc"
:
"简单构建可视化、可运营、可改进的 AI 应用"
,
reset
:
'请运行以下命令重置密码'
,
"tos"
:
"使用协议"
,
withGitHub
:
'使用 GitHub 登录'
,
"pp"
:
"隐私政策"
,
withGoogle
:
'使用 Google 登录'
,
"tosDesc"
:
"使用即代表你并同意我们的"
,
rightTitle
:
'释放大型语言模型的全部潜能'
,
"donthave"
:
"还没有邀请码?"
,
rightDesc
:
'简单构建可视化、可运营、可改进的 AI 应用'
,
"invalidInvitationCode"
:
"无效的邀请码"
,
tos
:
'使用协议'
,
"accountAlreadyInited"
:
"账户已经初始化"
,
pp
:
'隐私政策'
,
"error"
:
{
tosDesc
:
'使用即代表你并同意我们的'
,
"emailEmpty"
:
"邮箱不能为空"
,
donthave
:
'还没有邀请码?'
,
"emailInValid"
:
"请输入有效的邮箱地址"
,
invalidInvitationCode
:
'无效的邀请码'
,
"nameEmpty"
:
"用户名不能为空"
,
accountAlreadyInited
:
'账户已经初始化'
,
"passwordEmpty"
:
"密码不能为空"
,
error
:
{
"passwordInvalid"
:
"密码必须包含字母和数字,且长度不小于8位"
,
emailEmpty
:
'邮箱不能为空'
,
}
emailInValid
:
'请输入有效的邮箱地址'
,
nameEmpty
:
'用户名不能为空'
,
passwordEmpty
:
'密码不能为空'
,
passwordInvalid
:
'密码必须包含字母和数字,且长度不小于8位'
,
},
license
:
{
tip
:
'启动 Dify 社区版之前, 请阅读 GitHub 上的'
,
link
:
'开源协议'
,
},
join
:
'加入'
,
joinTipStart
:
'邀请你加入'
,
joinTipEnd
:
'团队'
,
invalid
:
'链接已失效'
,
explore
:
'探索 Dify'
,
activatedTipStart
:
'您已加入'
,
activatedTipEnd
:
'团队'
,
activated
:
'现在登录'
,
}
}
export
default
translation
export
default
translation
web/service/common.ts
View file @
477f1b37
...
@@ -66,8 +66,8 @@ export const fetchAccountIntegrates: Fetcher<{ data: AccountIntegrate[] | null }
...
@@ -66,8 +66,8 @@ export const fetchAccountIntegrates: Fetcher<{ data: AccountIntegrate[] | null }
return
get
(
url
,
{
params
})
as
Promise
<
{
data
:
AccountIntegrate
[]
|
null
}
>
return
get
(
url
,
{
params
})
as
Promise
<
{
data
:
AccountIntegrate
[]
|
null
}
>
}
}
export
const
inviteMember
:
Fetcher
<
CommonResponse
&
{
account
:
Member
},
{
url
:
string
;
body
:
Record
<
string
,
any
>
}
>
=
({
url
,
body
})
=>
{
export
const
inviteMember
:
Fetcher
<
CommonResponse
&
{
account
:
Member
;
invite_url
:
string
},
{
url
:
string
;
body
:
Record
<
string
,
any
>
}
>
=
({
url
,
body
})
=>
{
return
post
(
url
,
{
body
})
as
Promise
<
CommonResponse
&
{
account
:
Member
}
>
return
post
(
url
,
{
body
})
as
Promise
<
CommonResponse
&
{
account
:
Member
;
invite_url
:
string
}
>
}
}
export
const
updateMemberRole
:
Fetcher
<
CommonResponse
,
{
url
:
string
;
body
:
Record
<
string
,
any
>
}
>
=
({
url
,
body
})
=>
{
export
const
updateMemberRole
:
Fetcher
<
CommonResponse
,
{
url
:
string
;
body
:
Record
<
string
,
any
>
}
>
=
({
url
,
body
})
=>
{
...
@@ -101,3 +101,11 @@ export const syncDataSourceNotion: Fetcher<CommonResponse, { url: string }> = ({
...
@@ -101,3 +101,11 @@ export const syncDataSourceNotion: Fetcher<CommonResponse, { url: string }> = ({
export
const
updateDataSourceNotionAction
:
Fetcher
<
CommonResponse
,
{
url
:
string
}
>
=
({
url
})
=>
{
export
const
updateDataSourceNotionAction
:
Fetcher
<
CommonResponse
,
{
url
:
string
}
>
=
({
url
})
=>
{
return
patch
(
url
)
as
Promise
<
CommonResponse
>
return
patch
(
url
)
as
Promise
<
CommonResponse
>
}
}
export
const
invitationCheck
:
Fetcher
<
CommonResponse
&
{
is_valid
:
boolean
;
workspace_name
:
string
},
{
url
:
string
;
params
:
{
workspace_id
:
string
;
email
:
string
;
token
:
string
}
}
>
=
({
url
,
params
})
=>
{
return
get
(
url
,
{
params
})
as
Promise
<
CommonResponse
&
{
is_valid
:
boolean
;
workspace_name
:
string
}
>
}
export
const
activateMember
:
Fetcher
<
CommonResponse
,
{
url
:
string
;
body
:
any
}
>
=
({
url
,
body
})
=>
{
return
post
(
url
,
{
body
})
as
Promise
<
CommonResponse
>
}
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