mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 01:48:04 +08:00
merge main
This commit is contained in:
@ -34,13 +34,12 @@ const MembersPage = () => {
|
||||
}
|
||||
const { locale } = useContext(I18n)
|
||||
|
||||
const { userProfile, currentWorkspace, isCurrentWorkspaceManager } = useAppContext()
|
||||
const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext()
|
||||
const { data, mutate } = useSWR({ url: '/workspaces/current/members' }, fetchMembers)
|
||||
const [inviteModalVisible, setInviteModalVisible] = useState(false)
|
||||
const [invitationResults, setInvitationResults] = useState<InvitationResult[]>([])
|
||||
const [invitedModalVisible, setInvitedModalVisible] = useState(false)
|
||||
const accounts = data?.accounts || []
|
||||
const owner = accounts.filter(account => account.role === 'owner')?.[0]?.email === userProfile.email
|
||||
const { plan, enableBilling } = useProviderContext()
|
||||
const isNotUnlimitedMemberPlan = enableBilling && plan.type !== Plan.team && plan.type !== Plan.enterprise
|
||||
const isMemberFull = enableBilling && isNotUnlimitedMemberPlan && accounts.length >= plan.total.teamMembers
|
||||
@ -109,8 +108,8 @@ const MembersPage = () => {
|
||||
<div className='shrink-0 flex items-center w-[104px] py-2 text-[13px] text-gray-700'>{dayjs(Number((account.last_active_at || account.created_at)) * 1000).locale(locale === 'zh-Hans' ? 'zh-cn' : 'en').fromNow()}</div>
|
||||
<div className='shrink-0 w-[96px] flex items-center'>
|
||||
{
|
||||
(owner && account.role !== 'owner')
|
||||
? <Operation member={account} onOperate={mutate} />
|
||||
((isCurrentWorkspaceOwner && account.role !== 'owner') || (isCurrentWorkspaceManager && !['owner', 'admin'].includes(account.role)))
|
||||
? <Operation member={account} operatorRole={currentWorkspace.role} onOperate={mutate} />
|
||||
: <div className='px-3 text-[13px] text-gray-700'>{RoleMap[account.role] || RoleMap.normal}</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@ -26,11 +26,13 @@ const itemDescClassName = `
|
||||
|
||||
type IOperationProps = {
|
||||
member: Member
|
||||
operatorRole: string
|
||||
onOperate: () => void
|
||||
}
|
||||
|
||||
const Operation = ({
|
||||
member,
|
||||
operatorRole,
|
||||
onOperate,
|
||||
}: IOperationProps) => {
|
||||
const { t } = useTranslation()
|
||||
@ -43,11 +45,20 @@ const Operation = ({
|
||||
dataset_operator: t('common.members.datasetOperator'),
|
||||
}
|
||||
const roleList = useMemo(() => {
|
||||
return [
|
||||
...['admin', 'editor', 'normal'],
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] : []),
|
||||
]
|
||||
}, [datasetOperatorEnabled])
|
||||
if (operatorRole === 'owner') {
|
||||
return [
|
||||
...['admin', 'editor', 'normal'],
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] : []),
|
||||
]
|
||||
}
|
||||
if (operatorRole === 'admin') {
|
||||
return [
|
||||
...['editor', 'normal'],
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] : []),
|
||||
]
|
||||
}
|
||||
return []
|
||||
}, [operatorRole, datasetOperatorEnabled])
|
||||
const { notify } = useContext(ToastContext)
|
||||
const toHump = (name: string) => name.replace(/_(\w)/g, (all, letter) => letter.toUpperCase())
|
||||
const handleDeleteMemberOrCancelInvitation = async () => {
|
||||
|
||||
@ -14,6 +14,7 @@ import PluginsNav from './plugins-nav'
|
||||
import ExploreNav from './explore-nav'
|
||||
import ToolsNav from './tools-nav'
|
||||
import GithubStar from './github-star'
|
||||
import LicenseNav from './license-env'
|
||||
import { WorkspaceProvider } from '@/context/workspace-context'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import LogoSite from '@/app/components/base/logo/logo-site'
|
||||
@ -61,29 +62,29 @@ const Header = () => {
|
||||
<Bars3Icon className="h-4 w-4 text-gray-500" />
|
||||
</div>}
|
||||
{!isMobile
|
||||
&& <div className='flex w-64 p-2 pl-3 gap-1.5 items-center shrink-0 self-stretch'>
|
||||
<Link href="/apps" className='flex w-8 h-8 items-center justify-center gap-2 shrink-0'>
|
||||
<LogoSite className='object-contain' />
|
||||
</Link>
|
||||
<div className='font-light text-divider-deep'>/</div>
|
||||
<div className='flex items-center gap-0.5'>
|
||||
<WorkspaceProvider>
|
||||
<WorkplaceSelector />
|
||||
</WorkspaceProvider>
|
||||
{enableBilling && (
|
||||
<div className='select-none'>
|
||||
<PremiumBadge color='blue' allowHover={true} onClick={handlePlanClick}>
|
||||
<SparklesSoft className='flex items-center py-[1px] pl-[3px] w-3.5 h-3.5 text-components-premium-badge-indigo-text-stop-0' />
|
||||
<div className='system-xs-medium'>
|
||||
<span className='p-1'>
|
||||
{t('billing.upgradeBtn.encourageShort')}
|
||||
</span>
|
||||
</div>
|
||||
</PremiumBadge>
|
||||
</div>
|
||||
)}
|
||||
&& <div className='flex w-64 p-2 pl-3 gap-1.5 items-center shrink-0 self-stretch'>
|
||||
<Link href="/apps" className='flex w-8 h-8 items-center justify-center gap-2 shrink-0'>
|
||||
<LogoSite className='object-contain' />
|
||||
</Link>
|
||||
<div className='font-light text-divider-deep'>/</div>
|
||||
<div className='flex items-center gap-0.5'>
|
||||
<WorkspaceProvider>
|
||||
<WorkplaceSelector />
|
||||
</WorkspaceProvider>
|
||||
{enableBilling && (
|
||||
<div className='select-none'>
|
||||
<PremiumBadge color='blue' allowHover={true} onClick={handlePlanClick}>
|
||||
<SparklesSoft className='flex items-center py-[1px] pl-[3px] w-3.5 h-3.5 text-components-premium-badge-indigo-text-stop-0' />
|
||||
<div className='system-xs-medium'>
|
||||
<span className='p-1'>
|
||||
{t('billing.upgradeBtn.encourageShort')}
|
||||
</span>
|
||||
</div>
|
||||
</PremiumBadge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
{isMobile && (
|
||||
@ -116,6 +117,7 @@ const Header = () => {
|
||||
</div>
|
||||
)}
|
||||
<div className='flex items-center shrink-0'>
|
||||
<LicenseNav />
|
||||
<EnvNav />
|
||||
<div className='mr-3'>
|
||||
<PluginsNav />
|
||||
|
||||
29
web/app/components/header/license-env/index.tsx
Normal file
29
web/app/components/header/license-env/index.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import AppContext from '@/context/app-context'
|
||||
import { LicenseStatus } from '@/types/feature'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContextSelector } from 'use-context-selector'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const LicenseNav = () => {
|
||||
const { t } = useTranslation()
|
||||
const systemFeatures = useContextSelector(AppContext, s => s.systemFeatures)
|
||||
|
||||
if (systemFeatures.license?.status === LicenseStatus.EXPIRING) {
|
||||
const expiredAt = systemFeatures.license?.expired_at
|
||||
const count = dayjs(expiredAt).diff(dayjs(), 'days')
|
||||
return <div className='px-2 py-1 mr-4 rounded-full bg-util-colors-orange-orange-50 border-util-colors-orange-orange-100 system-xs-medium text-util-colors-orange-orange-600'>
|
||||
{count <= 1 && <span>{t('common.license.expiring', { count })}</span>}
|
||||
{count > 1 && <span>{t('common.license.expiring_plural', { count })}</span>}
|
||||
</div>
|
||||
}
|
||||
if (systemFeatures.license.status === LicenseStatus.ACTIVE) {
|
||||
return <div className='px-2 py-1 mr-4 rounded-md bg-util-colors-indigo-indigo-50 border-util-colors-indigo-indigo-100 system-xs-medium text-util-colors-indigo-indigo-600'>
|
||||
Enterprise
|
||||
</div>
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export default LicenseNav
|
||||
Reference in New Issue
Block a user