| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- 'use client'
- import { useEffect, useState } from 'react'
- import { useTranslation } from 'react-i18next'
- import Modal from '@/app/components/base/modal'
- import Button from '@/app/components/base/button'
- import Checkbox from '@/app/components/base/checkbox'
- import Loading from '@/app/components/base/loading'
- import { fetchDownloadPermission, updateDownloadPermission } from '@/service/datasets'
- import { useToastContext } from '@/app/components/base/toast'
- type User = {
- id: string
- name: string
- email: string
- has_permission: boolean
- }
- type PermissionModalProps = {
- datasetId: string
- documentId: string
- documentName: string
- onClose: () => void
- }
- const PermissionModal = ({
- datasetId,
- documentId,
- documentName,
- onClose,
- }: PermissionModalProps) => {
- const { t } = useTranslation()
- const { notify } = useToastContext()
- const [loading, setLoading] = useState(true)
- const [saving, setSaving] = useState(false)
- const [isRestricted, setIsRestricted] = useState(false)
- const [users, setUsers] = useState<User[]>([])
- // Load permission configuration
- useEffect(() => {
- loadPermission()
- }, [])
- const loadPermission = async () => {
- try {
- const data = await fetchDownloadPermission(datasetId, documentId)
- setIsRestricted(data.is_restricted)
- setUsers(data.users)
- }
- catch (error) {
- notify({ type: 'error', message: t('common.actionMsg.loadFailed') })
- }
- finally {
- setLoading(false)
- }
- }
- const handleSave = async () => {
- setSaving(true)
- try {
- // If not restricted, pass empty array; otherwise pass selected user IDs
- const userIds = isRestricted
- ? users.filter(u => u.has_permission).map(u => u.id)
- : []
- await updateDownloadPermission(datasetId, documentId, {
- is_restricted: isRestricted,
- user_ids: userIds,
- })
- notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
- onClose()
- }
- catch (error) {
- notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
- }
- finally {
- setSaving(false)
- }
- }
- const toggleUser = (userId: string) => {
- setUsers(users.map(u =>
- u.id === userId ? { ...u, has_permission: !u.has_permission } : u,
- ))
- }
- const selectAll = () => {
- setUsers(users.map(u => ({ ...u, has_permission: true })))
- }
- const deselectAll = () => {
- setUsers(users.map(u => ({ ...u, has_permission: false })))
- }
- const selectedCount = users.filter(u => u.has_permission).length
- return (
- <Modal
- isShow
- onClose={onClose}
- title={
- <div>
- <div className="text-lg font-semibold text-gray-900">
- {t('dataset.downloadPermission.title')}
- </div>
- <div className="text-sm font-normal text-gray-500 mt-1 truncate max-w-[500px]">
- {documentName}
- </div>
- </div>
- }
- className="w-[600px]"
- >
- {loading
- ? (
- <div className="flex items-center justify-center py-12">
- <Loading />
- </div>
- )
- : (
- <>
- <div className="py-4 space-y-6">
- {/* Enable restriction toggle */}
- <div className="flex items-start justify-between">
- <div className="flex-1">
- <div className="text-sm font-medium text-gray-900">
- {t('dataset.downloadPermission.enableRestriction')}
- </div>
- <div className="text-xs text-gray-500 mt-1">
- {t('dataset.downloadPermission.enableRestrictionDesc')}
- </div>
- </div>
- <div className="ml-4 flex-shrink-0">
- <Checkbox
- checked={isRestricted}
- onCheck={() => setIsRestricted(!isRestricted)}
- />
- </div>
- </div>
- {/* User list */}
- {isRestricted && (
- <div>
- <div className="flex items-center justify-between mb-3">
- <div className="text-sm font-medium text-gray-900">
- {t('dataset.downloadPermission.selectUsers')}
- </div>
- <div className="flex gap-2 text-xs">
- <button
- onClick={selectAll}
- className="text-primary-600 hover:text-primary-700"
- >
- {t('common.operation.selectAll')}
- </button>
- <span className="text-gray-300">|</span>
- <button
- onClick={deselectAll}
- className="text-gray-600 hover:text-gray-700"
- >
- {t('common.operation.deselectAll')}
- </button>
- </div>
- </div>
- <div className="border border-gray-200 rounded-lg max-h-[400px] overflow-y-auto">
- {users.length === 0
- ? (
- <div className="py-8 text-center text-sm text-gray-500">
- {t('dataset.downloadPermission.noUsers')}
- </div>
- )
- : (
- <div className="divide-y divide-gray-100">
- {users.map(user => (
- <label
- key={user.id}
- className="flex items-center gap-3 px-4 py-3 hover:bg-gray-50 cursor-pointer transition-colors"
- >
- <Checkbox
- checked={user.has_permission}
- onCheck={() => toggleUser(user.id)}
- />
- <div className="flex-1 min-w-0">
- <div className="text-sm font-medium text-gray-900 truncate">
- {user.name}
- </div>
- <div className="text-xs text-gray-500 truncate">
- {user.email}
- </div>
- </div>
- </label>
- ))}
- </div>
- )}
- </div>
- {users.length > 0 && (
- <div className="mt-2 text-xs text-gray-500">
- {t('dataset.downloadPermission.selectedCount', {
- count: selectedCount,
- total: users.length,
- })}
- </div>
- )}
- </div>
- )}
- </div>
- {/* Footer buttons */}
- <div className="flex justify-end gap-2 pt-4 border-t border-gray-200">
- <Button onClick={onClose}>
- {t('common.operation.cancel')}
- </Button>
- <Button
- variant="primary"
- onClick={handleSave}
- disabled={saving}
- loading={saving}
- >
- {t('common.operation.save')}
- </Button>
- </div>
- </>
- )}
- </Modal>
- )
- }
- export default PermissionModal
|