'use client' import type { AccessPolicyMemberBindingRemoval } from '@/app/components/access-rules-editor' import { useSuspenseQuery } from '@tanstack/react-query' import { useAtomValue } from 'jotai' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import AccessRulesEditor from '@/app/components/access-rules-editor' import Loading from '@/app/components/base/loading' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useLocale } from '@/context/i18n' import { workspacePermissionKeysAtom } from '@/context/permission-state' import { userProfileQueryOptions } from '@/features/account-profile/client' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import { getAccessControlTemplateLanguage } from '@/i18n-config/language' import { RESOURCE_ACCESS_SETTINGS_PAGE_SIZE } from '@/service/access-control/constants' import { useDatasetAccessRules, useDatasetResourceWhitelist, useDatasetResourceWhitelistConfig, useDatasetUserAccessSettings, useRemoveDatasetAccessPolicyMemberBindings, useUpdateDatasetAutomaticIncludeWorkspaceMembers, useUpdateDatasetUserAccessSettings, } from '@/service/access-control/use-dataset-access-config' import { getDatasetACLCapabilities } from '@/utils/permission' type DatasetAccessConfigPageProps = { datasetId: string } const DatasetAccessConfigPage = ({ datasetId }: DatasetAccessConfigPageProps) => { const { t } = useTranslation() const locale = useLocale() const language = useMemo(() => getAccessControlTemplateLanguage(locale), [locale]) const [currentPage, setCurrentPage] = useState(1) const [pageSize, setPageSize] = useState(RESOURCE_ACCESS_SETTINGS_PAGE_SIZE) const dataset = useDatasetDetailContextWithSelector((state) => state.dataset) const { data: currentUserId } = useSuspenseQuery({ ...userProfileQueryOptions(), select: (data) => data.profile.id, }) const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom) const { data: isRbacEnabled } = useSuspenseQuery({ ...systemFeaturesQueryOptions(), select: ({ rbac_enabled }) => rbac_enabled, }) const canAccessConfig = getDatasetACLCapabilities(dataset?.permission_keys, { currentUserId, resourceMaintainer: dataset?.maintainer, workspacePermissionKeys, isRbacEnabled, }).canAccessConfig const { data: datasetAccessRulesResponse, isLoading: isLoadingDatasetAccessRules } = useDatasetAccessRules(datasetId, language, { enabled: canAccessConfig }) const { data: datasetUserAccessSettingsResponse, isLoading: isLoadingDatasetUserAccessSettings, isPlaceholderData: isChangingDatasetUserAccessSettingsPage, } = useDatasetUserAccessSettings(datasetId, language, currentPage, pageSize, { enabled: canAccessConfig, }) const { data: datasetResourceWhitelist } = useDatasetResourceWhitelist(datasetId, { enabled: canAccessConfig, }) const { data: datasetResourceWhitelistConfig } = useDatasetResourceWhitelistConfig(datasetId, { enabled: canAccessConfig, }) const { mutate: updateDatasetAutomaticIncludeWorkspaceMembers, isPending: isUpdatingDatasetAutomaticIncludeWorkspaceMembers, } = useUpdateDatasetAutomaticIncludeWorkspaceMembers(datasetId) const { mutate: updateDatasetUserAccessSettings } = useUpdateDatasetUserAccessSettings(datasetId) const { mutate: removeDatasetAccessPolicyMemberBindings, mutateAsync: removeDatasetAccessPolicyMemberBindingsAsync, } = useRemoveDatasetAccessPolicyMemberBindings(datasetId) const [ optimisticAutomaticIncludeWorkspaceMembers, setOptimisticAutomaticIncludeWorkspaceMembers, ] = useState(null) const [updatingAccountId, setUpdatingAccountId] = useState(null) const datasetAccessRules = datasetAccessRulesResponse?.items || [] const datasetUserAccessSettings = datasetUserAccessSettingsResponse?.data ?? [] const datasetUserAccessSettingsPagination = datasetUserAccessSettingsResponse?.pagination const isLoadingEmptyPage = datasetUserAccessSettings.length === 0 && (isChangingDatasetUserAccessSettingsPage || (currentPage > 1 && datasetUserAccessSettingsPagination !== undefined && currentPage > datasetUserAccessSettingsPagination.total_pages)) const automaticIncludeWorkspaceMembers = optimisticAutomaticIncludeWorkspaceMembers ?? datasetResourceWhitelistConfig?.automatic_include_workspace_members const handleAutomaticIncludeWorkspaceMembersChange = useCallback( (nextValue: boolean) => { if (!canAccessConfig) return if (nextValue === automaticIncludeWorkspaceMembers) return if (!nextValue) setCurrentPage(1) setOptimisticAutomaticIncludeWorkspaceMembers(nextValue) updateDatasetAutomaticIncludeWorkspaceMembers(nextValue, { onError: () => setOptimisticAutomaticIncludeWorkspaceMembers(null), onSuccess: () => setOptimisticAutomaticIncludeWorkspaceMembers(null), }) }, [ automaticIncludeWorkspaceMembers, canAccessConfig, updateDatasetAutomaticIncludeWorkspaceMembers, ], ) const handleUserAccessPoliciesChange = useCallback( (accountId: string, accessPolicyIds: string[]) => { if (!canAccessConfig) return setUpdatingAccountId(accountId) updateDatasetUserAccessSettings( { accountId, accessPolicyIds }, { onSettled: () => setUpdatingAccountId(null) }, ) }, [canAccessConfig, updateDatasetUserAccessSettings], ) const handleAddAccessSubject = useCallback( (accountId: string, accessPolicyIds: string[]) => { if (!canAccessConfig) return if (automaticIncludeWorkspaceMembers) return const existingAccountCount = datasetUserAccessSettingsPagination?.total_count ?? datasetResourceWhitelist?.account_ids?.length ?? 0 const lastPageAfterAdd = Math.max(Math.ceil((existingAccountCount + 1) / pageSize), 1) setUpdatingAccountId(accountId) updateDatasetUserAccessSettings( { accountId, accessPolicyIds }, { onSuccess: () => setCurrentPage(lastPageAfterAdd), onSettled: () => setUpdatingAccountId(null), }, ) }, [ canAccessConfig, datasetResourceWhitelist?.account_ids?.length, datasetUserAccessSettingsPagination?.total_count, automaticIncludeWorkspaceMembers, pageSize, updateDatasetUserAccessSettings, ], ) const handlePageSizeChange = useCallback((nextPageSize: number) => { setCurrentPage(1) setPageSize(nextPageSize) }, []) const handleRemoveAccessPolicyMemberBinding = useCallback( (accountId: string, accessPolicyId: string) => { if (!canAccessConfig) return if (automaticIncludeWorkspaceMembers) return const shouldReturnToPreviousPage = currentPage > 1 && currentPage === datasetUserAccessSettingsPagination?.total_pages && datasetUserAccessSettings.length === 1 setUpdatingAccountId(accountId) removeDatasetAccessPolicyMemberBindings([{ accessPolicyId, accountIds: [accountId] }], { onSuccess: () => { if (shouldReturnToPreviousPage) { setCurrentPage((page) => (page === currentPage ? page - 1 : page)) } }, onSettled: () => setUpdatingAccountId(null), }) }, [ automaticIncludeWorkspaceMembers, canAccessConfig, currentPage, datasetUserAccessSettings.length, datasetUserAccessSettingsPagination?.total_pages, removeDatasetAccessPolicyMemberBindings, ], ) const handleBatchRemoveAccessPolicyMemberBindings = useCallback( async (removals: AccessPolicyMemberBindingRemoval[]) => { if (!canAccessConfig || automaticIncludeWorkspaceMembers) return const removedAccountCount = removals.reduce( (count, removal) => count + removal.accountIds.length, 0, ) const shouldReturnToPreviousPage = currentPage > 1 && currentPage === datasetUserAccessSettingsPagination?.total_pages && removedAccountCount >= datasetUserAccessSettings.length await removeDatasetAccessPolicyMemberBindingsAsync(removals) if (shouldReturnToPreviousPage) { setCurrentPage((page) => (page === currentPage ? page - 1 : page)) } }, [ automaticIncludeWorkspaceMembers, canAccessConfig, currentPage, datasetUserAccessSettings.length, datasetUserAccessSettingsPagination?.total_pages, removeDatasetAccessPolicyMemberBindingsAsync, ], ) if (!canAccessConfig) return return (

{t(($) => $['settings.resourceAccess'], { ns: 'common' })}

{t(($) => $['accessRule.datasetDescription'], { ns: 'permission' })}

) } export default DatasetAccessConfigPage