merge main

This commit is contained in:
Joel
2024-11-20 18:24:03 +08:00
241 changed files with 4972 additions and 1950 deletions

View File

@ -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>

View File

@ -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 () => {

View File

@ -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 />

View 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