index.tsx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. 'use client'
  2. import { useState } from 'react'
  3. import useSWR from 'swr'
  4. import dayjs from 'dayjs'
  5. import 'dayjs/locale/zh-cn'
  6. import relativeTime from 'dayjs/plugin/relativeTime'
  7. import DetailModal from './detail-modal'
  8. import UserModal from './user-modal'
  9. import { useContext } from 'use-context-selector'
  10. import { RiAddLine } from '@remixicon/react'
  11. import { useTranslation } from 'react-i18next'
  12. import { delDept, fetchDepts } from '@/service/common'
  13. import I18n from '@/context/i18n'
  14. import { useAppContext } from '@/context/app-context'
  15. import LogoEmbeddedChatHeader from '@/app/components/base/logo/logo-embedded-chat-header'
  16. import { useProviderContext } from '@/context/provider-context'
  17. import { Plan } from '@/app/components/billing/type'
  18. import Button from '@/app/components/base/button'
  19. import UpgradeBtn from '@/app/components/billing/upgrade-btn'
  20. import { NUM_INFINITE } from '@/app/components/billing/config'
  21. import { LanguagesSupported } from '@/i18n/language'
  22. import cn from '@/utils/classnames'
  23. import Confirm from '@/app/components/base/confirm'
  24. import useTimestamp from '@/hooks/use-timestamp'
  25. import { Column as AntdColumn, Space as AntdSpace, Table as AntdTable } from 'antd'
  26. dayjs.extend(relativeTime)
  27. const DeptsPage = () => {
  28. const { t } = useTranslation()
  29. const { locale } = useContext(I18n)
  30. const { formatTime } = useTimestamp()
  31. const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager, systemFeatures } = useAppContext()
  32. const { data, mutate }: any = useSWR(
  33. {
  34. url: '/depts',
  35. params: {
  36. },
  37. },
  38. fetchDepts,
  39. )
  40. const [detailModalVisible, setDetailModalVisible] = useState(false)
  41. const [userModalVisible, setUserModalVisible] = useState(false)
  42. const [transfer, setTransfer] = useState<any>({
  43. mode: 'add',
  44. row: null,
  45. })
  46. const deptList = data?.data || []
  47. const { plan, enableBilling } = useProviderContext()
  48. const isNotUnlimitedMemberPlan = enableBilling && plan.type !== Plan.team && plan.type !== Plan.enterprise
  49. const isMemberFull = enableBilling && isNotUnlimitedMemberPlan && deptList.length >= plan.total.teamMembers
  50. const [showConfirmDelete, setShowConfirmDelete] = useState(false)
  51. const [row, setRow] = useState<any>({})
  52. const handleDel = async () => {
  53. try {
  54. await delDept({
  55. url: `/depts/${row.dept_id}`,
  56. body: {},
  57. })
  58. setShowConfirmDelete(false)
  59. mutate()
  60. }
  61. catch (e) { }
  62. }
  63. return (
  64. <>
  65. <div className='flex flex-col'>
  66. <div className='mb-4 flex items-center gap-3 rounded-xl border-l-[0.5px] border-t-[0.5px] border-divider-subtle bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1 p-3 pr-5'>
  67. <LogoEmbeddedChatHeader className='!h-12 !w-12' />
  68. <div className='grow'>
  69. <div className='system-md-semibold text-text-secondary'>{currentWorkspace?.name}</div>
  70. {enableBilling && (
  71. <div className='system-xs-medium mt-1 text-text-tertiary'>
  72. {isNotUnlimitedMemberPlan
  73. ? (
  74. <div className='flex space-x-1'>
  75. <div>{t('billing.plansCommon.member')}{locale !== LanguagesSupported[1] && deptList.length > 1 && 's'}</div>
  76. <div className=''>{deptList.length}</div>
  77. <div>/</div>
  78. <div>{plan.total.teamMembers === NUM_INFINITE ? t('billing.plansCommon.unlimited') : plan.total.teamMembers}</div>
  79. </div>
  80. )
  81. : (
  82. <div className='flex space-x-1'>
  83. <div>{deptList.length}</div>
  84. <div>{t('billing.plansCommon.memberAfter')}{locale !== LanguagesSupported[1] && deptList.length > 1 && 's'}</div>
  85. </div>
  86. )}
  87. </div>
  88. )}
  89. </div>
  90. {isMemberFull && (
  91. <UpgradeBtn className='mr-2' loc='member-invite' />
  92. )}
  93. <Button variant='primary' className={cn('shrink-0')} disabled={!isCurrentWorkspaceManager || isMemberFull}
  94. onClick={() => {
  95. setTransfer({ mode: 'add', row: null })
  96. setDetailModalVisible(true)
  97. }}>
  98. <RiAddLine className='mr-1 h-4 w-4' />
  99. 新增
  100. </Button>
  101. </div>
  102. <div className='overflow-visible lg:overflow-visible'>
  103. <AntdTable
  104. rowKey="dept_id"
  105. dataSource={deptList}
  106. pagination={false}
  107. >
  108. <AntdColumn title="部门名称" dataIndex="dept_name" key="dept_name"/>
  109. {/* <AntdColumn title="关联用户数量" dataIndex="relation" key="relation" align="center" width={120}/> */}
  110. <AntdColumn title="操作" key="action" align="center" width={100} render={(_: any, record: any) => (
  111. <AntdSpace size="middle">
  112. {!(record.children?.length > 0) && (
  113. <Button variant='ghost-accent' size='small' className={cn('shrink-0')}
  114. onClick={() => {
  115. setTransfer({
  116. mode: 'user',
  117. row: JSON.parse(JSON.stringify(record)),
  118. })
  119. setUserModalVisible(true)
  120. }}>
  121. 关联用户
  122. </Button>
  123. )}
  124. <Button variant='ghost-accent' size='small' className={cn('shrink-0')} disabled={!isCurrentWorkspaceManager || isMemberFull}
  125. onClick={() => {
  126. setTransfer({
  127. mode: 'edit',
  128. row: JSON.parse(JSON.stringify(record)),
  129. })
  130. setDetailModalVisible(true)
  131. }}>
  132. 编辑
  133. </Button>
  134. {!(record.children?.length > 0) && !(record.relation > 0) && (
  135. <Button variant='ghost' size='small' className={cn('shrink-0 text-red-600')} disabled={!isCurrentWorkspaceManager || isMemberFull} onClick={() => {
  136. setRow(record)
  137. setShowConfirmDelete(true)
  138. }}>
  139. 刪除
  140. </Button>
  141. )}
  142. </AntdSpace>
  143. )}/>
  144. </AntdTable>
  145. </div>
  146. </div>
  147. {
  148. detailModalVisible && (
  149. <DetailModal
  150. transfer={transfer}
  151. onCancel={() => setDetailModalVisible(false)}
  152. onSend={() => {
  153. mutate()
  154. }}
  155. />
  156. )
  157. }
  158. {
  159. userModalVisible && (
  160. <UserModal
  161. transfer={transfer}
  162. onCancel={() => {
  163. setUserModalVisible(false)
  164. mutate()
  165. }}
  166. onSend={() => mutate()}
  167. />
  168. )
  169. }
  170. {showConfirmDelete && (
  171. <Confirm
  172. title="删除确认"
  173. content={`请确认是否删除${row.dept_name}?`}
  174. isShow={showConfirmDelete}
  175. onConfirm={handleDel}
  176. onCancel={() => setShowConfirmDelete(false)}
  177. />
  178. )}
  179. </>
  180. )
  181. }
  182. export default DeptsPage