index.tsx 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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 { delKnowledge, 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: '/external_applications',
  35. params: {
  36. page: 1,
  37. limit: 99999,
  38. },
  39. },
  40. fetchDepts,
  41. )
  42. const [detailModalVisible, setDetailModalVisible] = useState(false)
  43. const [userModalVisible, setUserModalVisible] = useState(false)
  44. const [transfer, setTransfer] = useState<any>({
  45. mode: 'add',
  46. row: null,
  47. })
  48. const deptList = data?.data || []
  49. const { plan, enableBilling } = useProviderContext()
  50. const isNotUnlimitedMemberPlan = enableBilling && plan.type !== Plan.team && plan.type !== Plan.enterprise
  51. const isMemberFull = enableBilling && isNotUnlimitedMemberPlan && deptList.length >= plan.total.teamMembers
  52. const [showConfirmDelete, setShowConfirmDelete] = useState(false)
  53. const [row, setRow] = useState<any>({})
  54. const handleDel = async () => {
  55. try {
  56. await delKnowledge({
  57. url: `/external_applications/${row.id}`,
  58. body: {},
  59. })
  60. setShowConfirmDelete(false)
  61. mutate()
  62. }
  63. catch (e) { }
  64. }
  65. return (
  66. <>
  67. <div className='flex flex-col'>
  68. <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'>
  69. <LogoEmbeddedChatHeader className='!h-12 !w-12' />
  70. <div className='grow'>
  71. <div className='system-md-semibold text-text-secondary'>{currentWorkspace?.name}</div>
  72. {enableBilling && (
  73. <div className='system-xs-medium mt-1 text-text-tertiary'>
  74. {isNotUnlimitedMemberPlan
  75. ? (
  76. <div className='flex space-x-1'>
  77. <div>{t('billing.plansCommon.member')}{locale !== LanguagesSupported[1] && deptList.length > 1 && 's'}</div>
  78. <div className=''>{deptList.length}</div>
  79. <div>/</div>
  80. <div>{plan.total.teamMembers === NUM_INFINITE ? t('billing.plansCommon.unlimited') : plan.total.teamMembers}</div>
  81. </div>
  82. )
  83. : (
  84. <div className='flex space-x-1'>
  85. <div>{deptList.length}</div>
  86. <div>{t('billing.plansCommon.memberAfter')}{locale !== LanguagesSupported[1] && deptList.length > 1 && 's'}</div>
  87. </div>
  88. )}
  89. </div>
  90. )}
  91. </div>
  92. {isMemberFull && (
  93. <UpgradeBtn className='mr-2' loc='member-invite' />
  94. )}
  95. <Button variant='primary' className={cn('shrink-0')} disabled={!isCurrentWorkspaceManager || isMemberFull}
  96. onClick={() => {
  97. setTransfer({ mode: 'add', row: null })
  98. setDetailModalVisible(true)
  99. }}>
  100. <RiAddLine className='mr-1 h-4 w-4' />
  101. 新增
  102. </Button>
  103. </div>
  104. <div className='overflow-visible lg:overflow-visible'>
  105. <AntdTable
  106. rowKey="id"
  107. dataSource={deptList}
  108. pagination={false}
  109. >
  110. <AntdColumn title="部门名称" dataIndex="name" key="name"/>
  111. <AntdColumn title="关联用户数量" dataIndex="relation" key="relation" align="center" width={120}/>
  112. <AntdColumn title="操作" key="action" align="center" width={100} render={(_: any, record: any) => (
  113. <AntdSpace size="middle">
  114. {!(record.children?.length > 0) && (
  115. <Button variant='ghost-accent' size='small' className={cn('shrink-0')}
  116. onClick={() => {
  117. setTransfer({
  118. mode: 'user',
  119. row: JSON.parse(JSON.stringify(record)),
  120. })
  121. setUserModalVisible(true)
  122. }}>
  123. 关联用户
  124. </Button>
  125. )}
  126. <Button variant='ghost-accent' size='small' className={cn('shrink-0')} disabled={!isCurrentWorkspaceManager || isMemberFull}
  127. onClick={() => {
  128. setTransfer({
  129. mode: 'edit',
  130. row: JSON.parse(JSON.stringify(record)),
  131. })
  132. setDetailModalVisible(true)
  133. }}>
  134. 编辑
  135. </Button>
  136. {!(record.children?.length > 0) && !(record.relation > 0) && (
  137. <Button variant='ghost' size='small' className={cn('shrink-0 text-red-600')} disabled={!isCurrentWorkspaceManager || isMemberFull} onClick={() => {
  138. setRow(record)
  139. setShowConfirmDelete(true)
  140. }}>
  141. 刪除
  142. </Button>
  143. )}
  144. </AntdSpace>
  145. )}/>
  146. </AntdTable>
  147. {/* <div className='flex min-w-[480px] items-center border-b border-divider-regular py-[7px]'> */}
  148. {/* <div className='system-xs-medium-uppercase shrink-0 grow text-text-tertiary'>部门名称</div> */}
  149. {/* <div className='system-xs-medium-uppercase shrink-0 w-[200px] text-center text-text-tertiary'>关联用户数量</div> */}
  150. {/* <div className='system-xs-medium-uppercase w-[200px] shrink-0 px-3 text-center text-text-tertiary'>操作</div> */}
  151. {/* </div> */}
  152. {/* <div className='relative min-w-[480px]'> */}
  153. {/* { */}
  154. {/* deptList.map((dept: any) => ( */}
  155. {/* <div key={dept.id} className='flex justify-between border-b border-divider-subtle'> */}
  156. {/* <div className='system-sm-regular shrink-0 grow py-2 text-text-secondary'>{dept.name}</div> */}
  157. {/* <div className='system-sm-regular shrink-0 w-[200px] py-2 text-center text-text-secondary'>0</div> */}
  158. {/* <div className='flex w-[200px] shrink-0 items-center justify-center'> */}
  159. {/* <Button variant='ghost-accent' size='small' className={cn('shrink-0')} disabled={!isCurrentWorkspaceManager || isMemberFull} */}
  160. {/* onClick={() => { */}
  161. {/* setTransfer({ */}
  162. {/* mode: 'user', */}
  163. {/* row: JSON.parse(JSON.stringify(dept)), */}
  164. {/* }) */}
  165. {/* setUserModalVisible(true) */}
  166. {/* }}> */}
  167. {/* 关联用户 */}
  168. {/* </Button> */}
  169. {/* <Button variant='ghost-accent' size='small' className={cn('shrink-0')} disabled={!isCurrentWorkspaceManager || isMemberFull} */}
  170. {/* onClick={() => { */}
  171. {/* setTransfer({ */}
  172. {/* mode: 'edit', */}
  173. {/* row: JSON.parse(JSON.stringify(dept)), */}
  174. {/* }) */}
  175. {/* setDetailModalVisible(true) */}
  176. {/* }}> */}
  177. {/* 编辑 */}
  178. {/* </Button> */}
  179. {/* <Button variant='ghost' size='small' className={cn('shrink-0 text-red-600')} disabled={!isCurrentWorkspaceManager || isMemberFull} onClick={() => { */}
  180. {/* setRow(dept) */}
  181. {/* setShowConfirmDelete(true) */}
  182. {/* }}> */}
  183. {/* 刪除 */}
  184. {/* </Button> */}
  185. {/* </div> */}
  186. {/* </div> */}
  187. {/* )) */}
  188. {/* } */}
  189. {/* </div> */}
  190. </div>
  191. </div>
  192. {
  193. detailModalVisible && (
  194. <DetailModal
  195. transfer={transfer}
  196. onCancel={() => setDetailModalVisible(false)}
  197. onSend={() => {
  198. mutate()
  199. }}
  200. />
  201. )
  202. }
  203. {
  204. userModalVisible && (
  205. <UserModal
  206. transfer={transfer}
  207. onCancel={() => {
  208. setUserModalVisible(false)
  209. mutate()
  210. }}
  211. onSend={() => mutate()}
  212. />
  213. )
  214. }
  215. {showConfirmDelete && (
  216. <Confirm
  217. title="删除确认"
  218. content={`请确认是否删除${row.name}?`}
  219. isShow={showConfirmDelete}
  220. onConfirm={handleDel}
  221. onCancel={() => setShowConfirmDelete(false)}
  222. />
  223. )}
  224. </>
  225. )
  226. }
  227. export default DeptsPage