import React, { useState, useEffect } from 'react'; import { Settings, Users, Loader, UserCheck, Trash2, Shield } from 'lucide-react'; import { ACCESS_ROLE_IDS, TPrincipal } from 'librechat-data-provider'; import { Button, OGDialog, OGDialogTitle, OGDialogClose, OGDialogContent, OGDialogTrigger, } from '~/components/ui'; import { cn, removeFocusOutlines } from '~/utils'; import { useToastContext } from '~/Providers'; import { useLocalize } from '~/hooks'; import { useGetAccessRolesQuery, useGetResourcePermissionsQuery, useUpdateResourcePermissionsMutation, } from 'librechat-data-provider/react-query'; import SelectedPrincipalsList from './PeoplePicker/SelectedPrincipalsList'; import PublicSharingToggle from './PublicSharingToggle'; export default function ManagePermissionsDialog({ agentDbId, agentName, resourceType = 'agent', onUpdatePermissions, }: { agentDbId: string; agentName?: string; resourceType?: string; onUpdatePermissions?: (shares: TPrincipal[], isPublic: boolean, publicRole: string) => void; }) { const localize = useLocalize(); const { showToast } = useToastContext(); const { data: permissionsData, isLoading: isLoadingPermissions, error: permissionsError, } = useGetResourcePermissionsQuery(resourceType, agentDbId, { enabled: !!agentDbId, }); const { data: accessRoles, // isLoading, } = useGetAccessRolesQuery(resourceType); const updatePermissionsMutation = useUpdateResourcePermissionsMutation(); const [managedShares, setManagedShares] = useState([]); const [managedIsPublic, setManagedIsPublic] = useState(false); const [managedPublicRole, setManagedPublicRole] = useState(ACCESS_ROLE_IDS.AGENT_VIEWER); const [isModalOpen, setIsModalOpen] = useState(false); const [hasChanges, setHasChanges] = useState(false); const currentShares: TPrincipal[] = permissionsData?.principals || []; const isPublic = permissionsData?.public || false; const publicRole = permissionsData?.publicAccessRoleId || ACCESS_ROLE_IDS.AGENT_VIEWER; useEffect(() => { if (permissionsData) { setManagedShares(currentShares); setManagedIsPublic(isPublic); setManagedPublicRole(publicRole); setHasChanges(false); } }, [permissionsData, isModalOpen]); if (!agentDbId) { return null; } if (permissionsError) { return
{localize('com_ui_permissions_failed_load')}
; } const handleRemoveShare = (idOnTheSource: string) => { setManagedShares(managedShares.filter((s) => s.idOnTheSource !== idOnTheSource)); setHasChanges(true); }; const handleRoleChange = (idOnTheSource: string, newRole: string) => { setManagedShares( managedShares.map((s) => s.idOnTheSource === idOnTheSource ? { ...s, accessRoleId: newRole } : s, ), ); setHasChanges(true); }; const handleSaveChanges = async () => { try { const originalSharesMap = new Map( currentShares.map((share) => [`${share.type}-${share.idOnTheSource}`, share]), ); const managedSharesMap = new Map( managedShares.map((share) => [`${share.type}-${share.idOnTheSource}`, share]), ); const updated = managedShares.filter((share) => { const key = `${share.type}-${share.idOnTheSource}`; const original = originalSharesMap.get(key); return !original || original.accessRoleId !== share.accessRoleId; }); const removed = currentShares.filter((share) => { const key = `${share.type}-${share.idOnTheSource}`; return !managedSharesMap.has(key); }); await updatePermissionsMutation.mutateAsync({ resourceType, resourceId: agentDbId, data: { updated, removed, public: managedIsPublic, publicAccessRoleId: managedIsPublic ? managedPublicRole : undefined, }, }); if (onUpdatePermissions) { onUpdatePermissions(managedShares, managedIsPublic, managedPublicRole); } showToast({ message: localize('com_ui_permissions_updated_success'), status: 'success', }); setIsModalOpen(false); } catch (error) { console.error('Error updating permissions:', error); showToast({ message: localize('com_ui_permissions_failed_update'), status: 'error', }); } }; const handleCancel = () => { setManagedShares(currentShares); setManagedIsPublic(isPublic); setManagedPublicRole(publicRole); setIsModalOpen(false); }; const handleRevokeAll = () => { setManagedShares([]); setManagedIsPublic(false); setHasChanges(true); }; const handlePublicToggle = (isPublic: boolean) => { setManagedIsPublic(isPublic); setHasChanges(true); if (!isPublic) { setManagedPublicRole(ACCESS_ROLE_IDS.AGENT_VIEWER); } }; const handlePublicRoleChange = (role: string) => { setManagedPublicRole(role); setHasChanges(true); }; const totalShares = managedShares.length + (managedIsPublic ? 1 : 0); const originalTotalShares = currentShares.length + (isPublic ? 1 : 0); /** Check if there's at least one owner (user, group, or public with owner role) */ const hasAtLeastOneOwner = managedShares.some((share) => share.accessRoleId === ACCESS_ROLE_IDS.AGENT_OWNER) || (managedIsPublic && managedPublicRole === ACCESS_ROLE_IDS.AGENT_OWNER); let peopleLabel = localize('com_ui_people'); if (managedShares.length === 1) { peopleLabel = localize('com_ui_person'); } let buttonAriaLabel = localize('com_ui_manage_permissions_for') + ' agent'; if (agentName != null && agentName !== '') { buttonAriaLabel = localize('com_ui_manage_permissions_for') + ` "${agentName}"`; } let dialogTitle = localize('com_ui_manage_permissions_for') + ' Agent'; if (agentName != null && agentName !== '') { dialogTitle = localize('com_ui_manage_permissions_for') + ` "${agentName}"`; } let publicSuffix = ''; if (managedIsPublic) { publicSuffix = localize('com_ui_and_public'); } return (
{dialogTitle}

{localize('com_ui_current_access')}

{(() => { if (totalShares === 0) { return localize('com_ui_no_users_groups_access'); } return localize('com_ui_shared_with_count', { 0: managedShares.length, 1: peopleLabel, 2: publicSuffix, }); })()}

{(managedShares.length > 0 || managedIsPublic) && ( )}
{(() => { if (isLoadingPermissions) { return (
{localize('com_ui_loading_permissions')}
); } if (managedShares.length > 0) { return (

{localize('com_ui_user_group_permissions')} ({managedShares.length})

handleRoleChange(id, newRole)} />
); } return (

{localize('com_ui_no_individual_access')}

); })()}

{localize('com_ui_public_access')}

{hasChanges && (
* {localize('com_ui_unsaved_changes')}
)} {!hasAtLeastOneOwner && hasChanges && (
* {localize('com_ui_at_least_one_owner_required')}
)}
); }