mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
🔧 refactor: Organize Sharing/Agent Components and Improve Type Safety
refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids, rename enums to PascalCase refactor: organize Sharing/Agent components, improve type safety for resource types and access role ids chore: move sharing related components to dedicated "Sharing" directory chore: remove PublicSharingToggle component and update index exports chore: move non-sidepanel agent components to `~/components/Agents` chore: move AgentCategoryDisplay component with tests chore: remove commented out code refactor: change PERMISSION_BITS from const to enum for better type safety refactor: reorganize imports in GenericGrantAccessDialog and update index exports for hooks refactor: update type definitions to use ACCESS_ROLE_IDS for improved type safety refactor: remove unused canAccessPromptResource middleware and related code refactor: remove unused prompt access roles from createAccessRoleMethods refactor: update resourceType in AclEntry type definition to remove unused 'prompt' value refactor: introduce ResourceType enum and update resourceType usage across data provider files for improved type safety refactor: update resourceType usage to ResourceType enum across sharing and permissions components for improved type safety refactor: standardize resourceType usage to ResourceType enum across agent and prompt models, permissions controller, and middleware for enhanced type safety refactor: update resourceType references from PROMPT_GROUP to PROMPTGROUP for consistency across models, middleware, and components refactor: standardize access role IDs and resource type usage across agent, file, and prompt models for improved type safety and consistency chore: add typedefs for TUpdateResourcePermissionsRequest and TUpdateResourcePermissionsResponse to enhance type definitions chore: move SearchPicker to PeoplePicker dir refactor: implement debouncing for query changes in SearchPicker for improved performance chore: fix typing, import order for agent admin settings fix: agent admin settings, prevent agent form submission refactor: rename `ACCESS_ROLE_IDS` to `AccessRoleIds` refactor: replace PermissionBits with PERMISSION_BITS refactor: replace PERMISSION_BITS with PermissionBits
This commit is contained in:
parent
ae732b2ebc
commit
81b32e400a
96 changed files with 781 additions and 798 deletions
|
|
@ -1,10 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
import type t from 'librechat-data-provider';
|
||||
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { renderAgentAvatar, getContactDisplayName } from '~/utils/agents';
|
||||
import { cn } from '~/utils';
|
||||
import { cn, renderAgentAvatar, getContactDisplayName } from '~/utils';
|
||||
|
||||
interface AgentCardProps {
|
||||
agent: t.Agent; // The agent data to display
|
||||
|
|
@ -6,7 +6,7 @@ import {
|
|||
QueryKeys,
|
||||
Constants,
|
||||
EModelEndpoint,
|
||||
PERMISSION_BITS,
|
||||
PermissionBits,
|
||||
LocalStorageKeys,
|
||||
AgentListResponse,
|
||||
} from 'librechat-data-provider';
|
||||
|
|
@ -45,7 +45,7 @@ const AgentDetail: React.FC<AgentDetailProps> = ({ agent, isOpen, onClose }) =>
|
|||
*/
|
||||
const handleStartChat = () => {
|
||||
if (agent) {
|
||||
const keys = [QueryKeys.agents, { requiredPermission: PERMISSION_BITS.EDIT }];
|
||||
const keys = [QueryKeys.agents, { requiredPermission: PermissionBits.EDIT }];
|
||||
const listResp = queryClient.getQueryData<AgentListResponse>(keys);
|
||||
if (listResp != null) {
|
||||
if (!listResp.data.some((a) => a.id === agent.id)) {
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { Button, Spinner } from '@librechat/client';
|
||||
import { PERMISSION_BITS } from 'librechat-data-provider';
|
||||
import { PermissionBits } from 'librechat-data-provider';
|
||||
import type t from 'librechat-data-provider';
|
||||
import { useMarketplaceAgentsInfiniteQuery } from '~/data-provider/Agents';
|
||||
import { useAgentCategories, useLocalize } from '~/hooks';
|
||||
|
|
@ -33,7 +33,7 @@ const AgentGrid: React.FC<AgentGridProps> = ({ category, searchQuery, onSelectAg
|
|||
limit: number;
|
||||
promoted?: 0 | 1;
|
||||
} = {
|
||||
requiredPermission: PERMISSION_BITS.VIEW, // View permission for marketplace viewing
|
||||
requiredPermission: PermissionBits.VIEW, // View permission for marketplace viewing
|
||||
limit: 6,
|
||||
};
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ import {
|
|||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from '@librechat/client';
|
||||
import { PERMISSION_BITS } from 'librechat-data-provider';
|
||||
import { PermissionBits } from 'librechat-data-provider';
|
||||
import type { TPromptGroup } from 'librechat-data-provider';
|
||||
import { useLocalize, useSubmitMessage, useCustomLink, useResourcePermissions } from '~/hooks';
|
||||
import VariableDialog from '~/components/Prompts/Groups/VariableDialog';
|
||||
|
|
@ -35,7 +35,7 @@ function ChatGroupItem({
|
|||
|
||||
// Check permissions for the promptGroup
|
||||
const { hasPermission } = useResourcePermissions('promptGroup', group._id || '');
|
||||
const canEdit = hasPermission(PERMISSION_BITS.EDIT);
|
||||
const canEdit = hasPermission(PermissionBits.EDIT);
|
||||
|
||||
const onCardClick: React.MouseEventHandler<HTMLButtonElement> = () => {
|
||||
const text = group.productionPrompt?.prompt;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { memo, useState, useRef, useMemo, useCallback, KeyboardEvent } from 'react';
|
||||
import { EarthIcon, Pen } from 'lucide-react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { PERMISSION_BITS, type TPromptGroup } from 'librechat-data-provider';
|
||||
import { PermissionBits, type TPromptGroup } from 'librechat-data-provider';
|
||||
import {
|
||||
Input,
|
||||
Label,
|
||||
|
|
@ -30,8 +30,8 @@ function DashGroupItemComponent({ group, instanceProjectId }: DashGroupItemProps
|
|||
const [nameInputValue, setNameInputValue] = useState(group.name);
|
||||
|
||||
const { hasPermission } = useResourcePermissions('promptGroup', group._id || '');
|
||||
const canEdit = hasPermission(PERMISSION_BITS.EDIT);
|
||||
const canDelete = hasPermission(PERMISSION_BITS.DELETE);
|
||||
const canEdit = hasPermission(PermissionBits.EDIT);
|
||||
const canDelete = hasPermission(PermissionBits.DELETE);
|
||||
|
||||
const isGlobalGroup = useMemo(
|
||||
() => instanceProjectId && group.projectIds?.includes(instanceProjectId),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Menu, Rocket } from 'lucide-react';
|
|||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import { useParams, useOutletContext } from 'react-router-dom';
|
||||
import { Button, Skeleton, useToastContext } from '@librechat/client';
|
||||
import { Permissions, PermissionTypes, PERMISSION_BITS } from 'librechat-data-provider';
|
||||
import { Permissions, PermissionTypes, PermissionBits } from 'librechat-data-provider';
|
||||
import type { TCreatePrompt, TPrompt, TPromptGroup } from 'librechat-data-provider';
|
||||
import {
|
||||
useGetPrompts,
|
||||
|
|
@ -186,8 +186,8 @@ const PromptForm = () => {
|
|||
group?._id || '',
|
||||
);
|
||||
|
||||
const canEdit = hasPermission(PERMISSION_BITS.EDIT);
|
||||
const canView = hasPermission(PERMISSION_BITS.VIEW);
|
||||
const canEdit = hasPermission(PermissionBits.EDIT);
|
||||
const canView = hasPermission(PermissionBits.VIEW);
|
||||
|
||||
const methods = useForm({
|
||||
defaultValues: {
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ import { Share2Icon } from 'lucide-react';
|
|||
import {
|
||||
SystemRoles,
|
||||
Permissions,
|
||||
ResourceType,
|
||||
PermissionTypes,
|
||||
PERMISSION_BITS,
|
||||
PermissionBits,
|
||||
} from 'librechat-data-provider';
|
||||
import { Button } from '@librechat/client';
|
||||
import type { TPromptGroup } from 'librechat-data-provider';
|
||||
|
|
@ -25,7 +26,7 @@ const SharePrompt = React.memo(
|
|||
// The query will be disabled if groupId is empty
|
||||
const groupId = group?._id || '';
|
||||
const { hasPermission, isLoading: permissionsLoading } = useResourcePermissions(
|
||||
'promptGroup',
|
||||
ResourceType.PROMPTGROUP,
|
||||
groupId,
|
||||
);
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ const SharePrompt = React.memo(
|
|||
return null;
|
||||
}
|
||||
|
||||
const canShareThisPrompt = hasPermission(PERMISSION_BITS.SHARE);
|
||||
const canShareThisPrompt = hasPermission(PermissionBits.SHARE);
|
||||
|
||||
const shouldShowShareButton =
|
||||
(group.author === user?.id || user?.role === SystemRoles.ADMIN || canShareThisPrompt) &&
|
||||
|
|
@ -49,7 +50,7 @@ const SharePrompt = React.memo(
|
|||
<GenericGrantAccessDialog
|
||||
resourceDbId={groupId}
|
||||
resourceName={group.name}
|
||||
resourceType="promptGroup"
|
||||
resourceType={ResourceType.PROMPTGROUP}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import * as Ariakit from '@ariakit/react';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { DropdownPopup } from '@librechat/client';
|
||||
import { ACCESS_ROLE_IDS } from 'librechat-data-provider';
|
||||
import { AccessRoleIds, ResourceType } from 'librechat-data-provider';
|
||||
import { useGetAccessRolesQuery } from 'librechat-data-provider/react-query';
|
||||
import type { AccessRole } from 'librechat-data-provider';
|
||||
import type * as t from '~/common';
|
||||
|
|
@ -10,15 +10,15 @@ import { cn, getRoleLocalizationKeys } from '~/utils';
|
|||
import { useLocalize } from '~/hooks';
|
||||
|
||||
interface AccessRolesPickerProps {
|
||||
resourceType?: string;
|
||||
selectedRoleId?: ACCESS_ROLE_IDS;
|
||||
onRoleChange: (roleId: ACCESS_ROLE_IDS) => void;
|
||||
resourceType?: ResourceType;
|
||||
selectedRoleId?: AccessRoleIds;
|
||||
onRoleChange: (roleId: AccessRoleIds) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function AccessRolesPicker({
|
||||
resourceType = 'agent',
|
||||
selectedRoleId = ACCESS_ROLE_IDS.AGENT_VIEWER,
|
||||
resourceType = ResourceType.AGENT,
|
||||
selectedRoleId = AccessRoleIds.AGENT_VIEWER,
|
||||
onRoleChange,
|
||||
className = '',
|
||||
}: AccessRolesPickerProps) {
|
||||
|
|
@ -27,7 +27,7 @@ export default function AccessRolesPicker({
|
|||
const { data: accessRoles, isLoading: rolesLoading } = useGetAccessRolesQuery(resourceType);
|
||||
|
||||
/** Helper function to get localized role name and description */
|
||||
const getLocalizedRoleInfo = (roleId: ACCESS_ROLE_IDS) => {
|
||||
const getLocalizedRoleInfo = (roleId: AccessRoleIds) => {
|
||||
const keys = getRoleLocalizationKeys(roleId);
|
||||
return {
|
||||
name: localize(keys.name),
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useState } from 'react';
|
||||
import { AccessRoleIds, ResourceType } from 'librechat-data-provider';
|
||||
import { Share2Icon, Users, Loader, Shield, Link, CopyCheck } from 'lucide-react';
|
||||
import {
|
||||
Button,
|
||||
|
|
@ -10,13 +11,17 @@ import {
|
|||
useToastContext,
|
||||
} from '@librechat/client';
|
||||
import type { TPrincipal } from 'librechat-data-provider';
|
||||
import { useLocalize, useCopyToClipboard } from '~/hooks';
|
||||
import { usePeoplePickerPermissions, useResourcePermissionState } from '~/hooks/Sharing';
|
||||
import {
|
||||
usePeoplePickerPermissions,
|
||||
useResourcePermissionState,
|
||||
useCopyToClipboard,
|
||||
useLocalize,
|
||||
} from '~/hooks';
|
||||
import GenericManagePermissionsDialog from './GenericManagePermissionsDialog';
|
||||
import PeoplePicker from '../SidePanel/Agents/Sharing/PeoplePicker/PeoplePicker';
|
||||
import AccessRolesPicker from '../SidePanel/Agents/Sharing/AccessRolesPicker';
|
||||
import PublicSharingToggle from './PublicSharingToggle';
|
||||
import AccessRolesPicker from './AccessRolesPicker';
|
||||
import { cn, removeFocusOutlines } from '~/utils';
|
||||
import { PeoplePicker } from './PeoplePicker';
|
||||
|
||||
export default function GenericGrantAccessDialog({
|
||||
resourceName,
|
||||
|
|
@ -30,8 +35,8 @@ export default function GenericGrantAccessDialog({
|
|||
resourceDbId?: string | null;
|
||||
resourceId?: string | null;
|
||||
resourceName?: string;
|
||||
resourceType: string;
|
||||
onGrantAccess?: (shares: TPrincipal[], isPublic: boolean, publicRole: string) => void;
|
||||
resourceType: ResourceType;
|
||||
onGrantAccess?: (shares: TPrincipal[], isPublic: boolean, publicRole: AccessRoleIds) => void;
|
||||
disabled?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
|
|
@ -55,8 +60,8 @@ export default function GenericGrantAccessDialog({
|
|||
} = useResourcePermissionState(resourceType, resourceDbId, isModalOpen);
|
||||
|
||||
const [newShares, setNewShares] = useState<TPrincipal[]>([]);
|
||||
const [defaultPermissionId, setDefaultPermissionId] = useState<string>(
|
||||
config?.defaultViewerRoleId ?? '',
|
||||
const [defaultPermissionId, setDefaultPermissionId] = useState<AccessRoleIds | undefined>(
|
||||
config?.defaultViewerRoleId,
|
||||
);
|
||||
|
||||
const resourceUrl = config?.getResourceUrl ? config?.getResourceUrl(resourceId || '') : '';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { TPrincipal } from 'librechat-data-provider';
|
||||
import { Settings, Users, Loader, UserCheck, Trash2, Shield } from 'lucide-react';
|
||||
import { useGetAccessRolesQuery } from 'librechat-data-provider/react-query';
|
||||
import { Settings, Users, Loader, UserCheck, Trash2, Shield } from 'lucide-react';
|
||||
import {
|
||||
Button,
|
||||
OGDialog,
|
||||
|
|
@ -11,9 +10,10 @@ import {
|
|||
OGDialogTrigger,
|
||||
useToastContext,
|
||||
} from '@librechat/client';
|
||||
import SelectedPrincipalsList from '../SidePanel/Agents/Sharing/PeoplePicker/SelectedPrincipalsList';
|
||||
import type { TPrincipal, ResourceType, AccessRoleIds } from 'librechat-data-provider';
|
||||
import { useResourcePermissionState } from '~/hooks/Sharing';
|
||||
import PublicSharingToggle from './PublicSharingToggle';
|
||||
import { SelectedPrincipalsList } from './PeoplePicker';
|
||||
import { cn, removeFocusOutlines } from '~/utils';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
|
|
@ -26,8 +26,12 @@ export default function GenericManagePermissionsDialog({
|
|||
}: {
|
||||
resourceDbId: string;
|
||||
resourceName?: string;
|
||||
resourceType: string;
|
||||
onUpdatePermissions?: (shares: TPrincipal[], isPublic: boolean, publicRole: string) => void;
|
||||
resourceType: ResourceType;
|
||||
onUpdatePermissions?: (
|
||||
shares: TPrincipal[],
|
||||
isPublic: boolean,
|
||||
publicRole: AccessRoleIds,
|
||||
) => void;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { ACCESS_ROLE_IDS, PermissionTypes, Permissions } from 'librechat-data-provider';
|
||||
import { Share2Icon, Users, Loader, Shield, Link, CopyCheck } from 'lucide-react';
|
||||
import { Permissions, ResourceType, PermissionTypes, AccessRoleIds } from 'librechat-data-provider';
|
||||
import {
|
||||
useGetResourcePermissionsQuery,
|
||||
useUpdateResourcePermissionsMutation,
|
||||
|
|
@ -18,22 +18,22 @@ import type { TPrincipal } from 'librechat-data-provider';
|
|||
import { useLocalize, useCopyToClipboard, useHasAccess } from '~/hooks';
|
||||
import ManagePermissionsDialog from './ManagePermissionsDialog';
|
||||
import PublicSharingToggle from './PublicSharingToggle';
|
||||
import PeoplePicker from './PeoplePicker/PeoplePicker';
|
||||
import AccessRolesPicker from './AccessRolesPicker';
|
||||
import { cn, removeFocusOutlines } from '~/utils';
|
||||
import { PeoplePicker } from './PeoplePicker';
|
||||
|
||||
export default function GrantAccessDialog({
|
||||
agentName,
|
||||
onGrantAccess,
|
||||
resourceType = 'agent',
|
||||
resourceType = ResourceType.AGENT,
|
||||
agentDbId,
|
||||
agentId,
|
||||
}: {
|
||||
agentDbId?: string | null;
|
||||
agentId?: string | null;
|
||||
agentName?: string;
|
||||
onGrantAccess?: (shares: TPrincipal[], isPublic: boolean, publicRole: string) => void;
|
||||
resourceType?: string;
|
||||
onGrantAccess?: (shares: TPrincipal[], isPublic: boolean, publicRole: AccessRoleIds) => void;
|
||||
resourceType?: ResourceType;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
|
|
@ -73,7 +73,7 @@ export default function GrantAccessDialog({
|
|||
|
||||
const [newShares, setNewShares] = useState<TPrincipal[]>([]);
|
||||
const [defaultPermissionId, setDefaultPermissionId] = useState<string>(
|
||||
ACCESS_ROLE_IDS.AGENT_VIEWER,
|
||||
AccessRoleIds.AGENT_VIEWER,
|
||||
);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isCopying, setIsCopying] = useState(false);
|
||||
|
|
@ -94,10 +94,10 @@ export default function GrantAccessDialog({
|
|||
})) || [];
|
||||
|
||||
const currentIsPublic = permissionsData?.public ?? false;
|
||||
const currentPublicRole = permissionsData?.publicAccessRoleId || ACCESS_ROLE_IDS.AGENT_VIEWER;
|
||||
const currentPublicRole = permissionsData?.publicAccessRoleId || AccessRoleIds.AGENT_VIEWER;
|
||||
|
||||
const [isPublic, setIsPublic] = useState(false);
|
||||
const [publicRole, setPublicRole] = useState<string>(ACCESS_ROLE_IDS.AGENT_VIEWER);
|
||||
const [publicRole, setPublicRole] = useState<AccessRoleIds>(AccessRoleIds.AGENT_VIEWER);
|
||||
|
||||
useEffect(() => {
|
||||
if (permissionsData && isModalOpen) {
|
||||
|
|
@ -140,9 +140,9 @@ export default function GrantAccessDialog({
|
|||
});
|
||||
|
||||
setNewShares([]);
|
||||
setDefaultPermissionId(ACCESS_ROLE_IDS.AGENT_VIEWER);
|
||||
setDefaultPermissionId(AccessRoleIds.AGENT_VIEWER);
|
||||
setIsPublic(false);
|
||||
setPublicRole(ACCESS_ROLE_IDS.AGENT_VIEWER);
|
||||
setPublicRole(AccessRoleIds.AGENT_VIEWER);
|
||||
setIsModalOpen(false);
|
||||
} catch (error) {
|
||||
console.error('Error granting access:', error);
|
||||
|
|
@ -155,9 +155,9 @@ export default function GrantAccessDialog({
|
|||
|
||||
const handleCancel = () => {
|
||||
setNewShares([]);
|
||||
setDefaultPermissionId(ACCESS_ROLE_IDS.AGENT_VIEWER);
|
||||
setDefaultPermissionId(AccessRoleIds.AGENT_VIEWER);
|
||||
setIsPublic(false);
|
||||
setPublicRole(ACCESS_ROLE_IDS.AGENT_VIEWER);
|
||||
setPublicRole(AccessRoleIds.AGENT_VIEWER);
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { ACCESS_ROLE_IDS, TPrincipal } from 'librechat-data-provider';
|
||||
import { AccessRoleIds, ResourceType } from 'librechat-data-provider';
|
||||
import { Settings, Users, Loader, UserCheck, Trash2, Shield } from 'lucide-react';
|
||||
import {
|
||||
useGetAccessRolesQuery,
|
||||
useGetResourcePermissionsQuery,
|
||||
useUpdateResourcePermissionsMutation,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import type { TPrincipal } from 'librechat-data-provider';
|
||||
import {
|
||||
Button,
|
||||
OGDialog,
|
||||
|
|
@ -15,21 +16,25 @@ import {
|
|||
OGDialogTrigger,
|
||||
useToastContext,
|
||||
} from '@librechat/client';
|
||||
import SelectedPrincipalsList from './PeoplePicker/SelectedPrincipalsList';
|
||||
import { SelectedPrincipalsList } from './PeoplePicker';
|
||||
import PublicSharingToggle from './PublicSharingToggle';
|
||||
import { cn, removeFocusOutlines } from '~/utils';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export default function ManagePermissionsDialog({
|
||||
agentDbId,
|
||||
agentName,
|
||||
resourceType = 'agent',
|
||||
resourceType = ResourceType.AGENT,
|
||||
agentDbId,
|
||||
onUpdatePermissions,
|
||||
}: {
|
||||
agentDbId: string;
|
||||
agentName?: string;
|
||||
resourceType?: string;
|
||||
onUpdatePermissions?: (shares: TPrincipal[], isPublic: boolean, publicRole: string) => void;
|
||||
resourceType?: ResourceType;
|
||||
onUpdatePermissions?: (
|
||||
shares: TPrincipal[],
|
||||
isPublic: boolean,
|
||||
publicRole: AccessRoleIds,
|
||||
) => void;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
|
|
@ -50,20 +55,22 @@ export default function ManagePermissionsDialog({
|
|||
|
||||
const [managedShares, setManagedShares] = useState<TPrincipal[]>([]);
|
||||
const [managedIsPublic, setManagedIsPublic] = useState(false);
|
||||
const [managedPublicRole, setManagedPublicRole] = useState<string>(ACCESS_ROLE_IDS.AGENT_VIEWER);
|
||||
const [managedPublicRole, setManagedPublicRole] = useState<AccessRoleIds>(
|
||||
AccessRoleIds.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;
|
||||
const publicRole = permissionsData?.publicAccessRoleId || AccessRoleIds.AGENT_VIEWER;
|
||||
|
||||
useEffect(() => {
|
||||
if (permissionsData) {
|
||||
const shares = permissionsData.principals || [];
|
||||
const isPublicValue = permissionsData.public || false;
|
||||
const publicRoleValue = permissionsData.publicAccessRoleId || ACCESS_ROLE_IDS.AGENT_VIEWER;
|
||||
const publicRoleValue = permissionsData.publicAccessRoleId || AccessRoleIds.AGENT_VIEWER;
|
||||
|
||||
setManagedShares(shares);
|
||||
setManagedIsPublic(isPublicValue);
|
||||
|
|
@ -85,7 +92,7 @@ export default function ManagePermissionsDialog({
|
|||
setHasChanges(true);
|
||||
};
|
||||
|
||||
const handleRoleChange = (idOnTheSource: string, newRole: string) => {
|
||||
const handleRoleChange = (idOnTheSource: string, newRole: AccessRoleIds) => {
|
||||
setManagedShares(
|
||||
managedShares.map((s) =>
|
||||
s.idOnTheSource === idOnTheSource ? { ...s, accessRoleId: newRole } : s,
|
||||
|
|
@ -160,10 +167,10 @@ export default function ManagePermissionsDialog({
|
|||
setManagedIsPublic(isPublic);
|
||||
setHasChanges(true);
|
||||
if (!isPublic) {
|
||||
setManagedPublicRole(ACCESS_ROLE_IDS.AGENT_VIEWER);
|
||||
setManagedPublicRole(AccessRoleIds.AGENT_VIEWER);
|
||||
}
|
||||
};
|
||||
const handlePublicRoleChange = (role: string) => {
|
||||
const handlePublicRoleChange = (role: AccessRoleIds) => {
|
||||
setManagedPublicRole(role);
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
|
@ -172,8 +179,8 @@ export default function ManagePermissionsDialog({
|
|||
|
||||
/** 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);
|
||||
managedShares.some((share) => share.accessRoleId === AccessRoleIds.AGENT_OWNER) ||
|
||||
(managedIsPublic && managedPublicRole === AccessRoleIds.AGENT_OWNER);
|
||||
|
||||
let peopleLabel = localize('com_ui_people');
|
||||
if (managedShares.length === 1) {
|
||||
|
|
@ -3,7 +3,7 @@ import type { TPrincipal, PrincipalSearchParams } from 'librechat-data-provider'
|
|||
import { useSearchPrincipalsQuery } from 'librechat-data-provider/react-query';
|
||||
import PeoplePickerSearchItem from './PeoplePickerSearchItem';
|
||||
import SelectedPrincipalsList from './SelectedPrincipalsList';
|
||||
import { SearchPicker } from '~/components/ui/SearchPicker';
|
||||
import { SearchPicker } from './SearchPicker';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
interface PeoplePickerProps {
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { Search, X } from 'lucide-react';
|
||||
import * as Ariakit from '@ariakit/react';
|
||||
import { Spinner, Skeleton } from '@librechat/client';
|
||||
|
|
@ -36,10 +37,31 @@ export function SearchPicker<TOption extends { key: string; value: string }>({
|
|||
const localize = useLocalize();
|
||||
const [_open, setOpen] = React.useState(false);
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
const [localQuery, setLocalQuery] = React.useState(query);
|
||||
const combobox = Ariakit.useComboboxStore({
|
||||
resetValueOnHide,
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
setLocalQuery(query);
|
||||
}, [query]);
|
||||
|
||||
const debouncedOnQueryChange = React.useMemo(
|
||||
() =>
|
||||
debounce((value: string) => {
|
||||
onQueryChange(value);
|
||||
}, 500),
|
||||
[onQueryChange],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
debouncedOnQueryChange.cancel();
|
||||
};
|
||||
}, [debouncedOnQueryChange]);
|
||||
|
||||
const onPickHandler = (option: TOption) => {
|
||||
setLocalQuery('');
|
||||
onQueryChange('');
|
||||
onPick(option);
|
||||
setOpen(false);
|
||||
|
|
@ -47,9 +69,11 @@ export function SearchPicker<TOption extends { key: string; value: string }>({
|
|||
inputRef.current.focus();
|
||||
}
|
||||
};
|
||||
const showClearIcon = query.trim().length > 0;
|
||||
const showClearIcon = localQuery.trim().length > 0;
|
||||
const clearText = () => {
|
||||
setLocalQuery('');
|
||||
onQueryChange('');
|
||||
debouncedOnQueryChange.cancel();
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
|
|
@ -77,7 +101,9 @@ export function SearchPicker<TOption extends { key: string; value: string }>({
|
|||
if (e.key === 'Escape' && combobox.getState().open) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setLocalQuery('');
|
||||
onQueryChange('');
|
||||
debouncedOnQueryChange.cancel();
|
||||
setOpen(false);
|
||||
}
|
||||
}}
|
||||
|
|
@ -85,9 +111,11 @@ export function SearchPicker<TOption extends { key: string; value: string }>({
|
|||
setValueOnClick={false}
|
||||
setValueOnChange={false}
|
||||
onChange={(e) => {
|
||||
onQueryChange(e.target.value);
|
||||
const value = e.target.value;
|
||||
setLocalQuery(value);
|
||||
debouncedOnQueryChange(value);
|
||||
}}
|
||||
value={query}
|
||||
value={localQuery}
|
||||
// autoSelect
|
||||
placeholder={placeholder || localize('com_ui_select_options')}
|
||||
className="m-0 mr-0 w-full rounded-md border-none bg-transparent p-0 py-2 pl-9 pr-3 text-sm leading-tight text-text-primary placeholder-text-secondary placeholder-opacity-100 focus:outline-none focus-visible:outline-none group-focus-within:placeholder-text-primary group-hover:placeholder-text-primary"
|
||||
|
|
@ -115,7 +143,7 @@ export function SearchPicker<TOption extends { key: string; value: string }>({
|
|||
open={
|
||||
isLoading ||
|
||||
options.length > 0 ||
|
||||
(query.trim().length >= minQueryLengthForNoResults && !isLoading)
|
||||
(localQuery.trim().length >= minQueryLengthForNoResults && !isLoading)
|
||||
}
|
||||
store={combobox}
|
||||
unmountOnHide
|
||||
|
|
@ -162,7 +190,7 @@ export function SearchPicker<TOption extends { key: string; value: string }>({
|
|||
));
|
||||
}
|
||||
|
||||
if (query.trim().length >= minQueryLengthForNoResults) {
|
||||
if (localQuery.trim().length >= minQueryLengthForNoResults) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
|
@ -2,7 +2,7 @@ import React, { useState, useId } from 'react';
|
|||
import * as Menu from '@ariakit/react/menu';
|
||||
import { Button, DropdownPopup } from '@librechat/client';
|
||||
import { Users, X, ExternalLink, ChevronDown } from 'lucide-react';
|
||||
import type { TPrincipal, TAccessRole, ACCESS_ROLE_IDS } from 'librechat-data-provider';
|
||||
import type { TPrincipal, TAccessRole, AccessRoleIds } from 'librechat-data-provider';
|
||||
import { getRoleLocalizationKeys } from '~/utils';
|
||||
import PrincipalAvatar from '../PrincipalAvatar';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
|
@ -10,7 +10,7 @@ import { useLocalize } from '~/hooks';
|
|||
interface SelectedPrincipalsListProps {
|
||||
principles: TPrincipal[];
|
||||
onRemoveHandler: (idOnTheSource: string) => void;
|
||||
onRoleChange?: (idOnTheSource: string, newRoleId: string) => void;
|
||||
onRoleChange?: (idOnTheSource: string, newRoleId: AccessRoleIds) => void;
|
||||
availableRoles?: Omit<TAccessRole, 'resourceType'>[];
|
||||
className?: string;
|
||||
}
|
||||
|
|
@ -98,8 +98,8 @@ export default function SelectedPrincipalsList({
|
|||
}
|
||||
|
||||
interface RoleSelectorProps {
|
||||
currentRole: ACCESS_ROLE_IDS;
|
||||
onRoleChange: (newRole: ACCESS_ROLE_IDS) => void;
|
||||
currentRole: AccessRoleIds;
|
||||
onRoleChange: (newRole: AccessRoleIds) => void;
|
||||
availableRoles: Omit<TAccessRole, 'resourceType'>[];
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ function RoleSelector({ currentRole, onRoleChange, availableRoles }: RoleSelecto
|
|||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const localize = useLocalize();
|
||||
|
||||
const getLocalizedRoleName = (roleId: ACCESS_ROLE_IDS) => {
|
||||
const getLocalizedRoleName = (roleId: AccessRoleIds) => {
|
||||
const keys = getRoleLocalizationKeys(roleId);
|
||||
return localize(keys.name);
|
||||
};
|
||||
3
client/src/components/Sharing/PeoplePicker/index.ts
Normal file
3
client/src/components/Sharing/PeoplePicker/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export { default as PeoplePicker } from './PeoplePicker';
|
||||
export { default as PeoplePickerSearchItem } from './PeoplePickerSearchItem';
|
||||
export { default as SelectedPrincipalsList } from './SelectedPrincipalsList';
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import { Globe, Shield } from 'lucide-react';
|
||||
import { Switch } from '@librechat/client';
|
||||
import AccessRolesPicker from '../SidePanel/Agents/Sharing/AccessRolesPicker';
|
||||
import { Globe, Shield } from 'lucide-react';
|
||||
import type { AccessRoleIds } from 'librechat-data-provider';
|
||||
import { ResourceType } from 'librechat-data-provider';
|
||||
import AccessRolesPicker from './AccessRolesPicker';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export default function PublicSharingToggle({
|
||||
|
|
@ -9,13 +11,13 @@ export default function PublicSharingToggle({
|
|||
publicRole,
|
||||
onPublicToggle,
|
||||
onPublicRoleChange,
|
||||
resourceType = 'agent',
|
||||
resourceType = ResourceType.AGENT,
|
||||
}: {
|
||||
isPublic: boolean;
|
||||
publicRole: string;
|
||||
publicRole: AccessRoleIds;
|
||||
onPublicToggle: (isPublic: boolean) => void;
|
||||
onPublicRoleChange: (role: string) => void;
|
||||
resourceType?: string;
|
||||
onPublicRoleChange: (role: AccessRoleIds) => void;
|
||||
resourceType?: ResourceType;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
export { default as AccessRolesPicker } from './AccessRolesPicker';
|
||||
export { default as GenericGrantAccessDialog } from './GenericGrantAccessDialog';
|
||||
export { default as GenericManagePermissionsDialog } from './GenericManagePermissionsDialog';
|
||||
export { default as ManagePermissionsDialog } from './ManagePermissionsDialog';
|
||||
export { default as PrincipalAvatar } from './PrincipalAvatar';
|
||||
export { default as PublicSharingToggle } from './PublicSharingToggle';
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import * as Ariakit from '@ariakit/react';
|
||||
import { useMemo, useEffect, useState } from 'react';
|
||||
import * as Ariakit from '@ariakit/react';
|
||||
import { ShieldEllipsis } from 'lucide-react';
|
||||
import { useForm, Controller } from 'react-hook-form';
|
||||
import { Permissions, SystemRoles, roleDefaults, PermissionTypes } from 'librechat-data-provider';
|
||||
import {
|
||||
Button,
|
||||
Switch,
|
||||
OGDialog,
|
||||
DropdownPopup,
|
||||
OGDialogTitle,
|
||||
OGDialogContent,
|
||||
OGDialogTrigger,
|
||||
Button,
|
||||
Switch,
|
||||
DropdownPopup,
|
||||
useToastContext,
|
||||
} from '@librechat/client';
|
||||
import type { Control, UseFormSetValue, UseFormGetValues } from 'react-hook-form';
|
||||
|
|
@ -64,8 +64,8 @@ const LabelController: React.FC<LabelControllerProps> = ({
|
|||
|
||||
const AdminSettings = () => {
|
||||
const localize = useLocalize();
|
||||
const { user, roles } = useAuthContext();
|
||||
const { showToast } = useToastContext();
|
||||
const { user, roles } = useAuthContext();
|
||||
const { mutate, isLoading } = useUpdateAgentPermissionsMutation({
|
||||
onSuccess: () => {
|
||||
showToast({ status: 'success', message: localize('com_ui_saved') });
|
||||
|
|
@ -79,8 +79,9 @@ const AdminSettings = () => {
|
|||
const [selectedRole, setSelectedRole] = useState<SystemRoles>(SystemRoles.USER);
|
||||
|
||||
const defaultValues = useMemo(() => {
|
||||
if (roles?.[selectedRole]?.permissions) {
|
||||
return roles[selectedRole].permissions[PermissionTypes.AGENTS];
|
||||
const rolePerms = roles?.[selectedRole]?.permissions;
|
||||
if (rolePerms) {
|
||||
return rolePerms[PermissionTypes.AGENTS];
|
||||
}
|
||||
return roleDefaults[selectedRole].permissions[PermissionTypes.AGENTS];
|
||||
}, [roles, selectedRole]);
|
||||
|
|
@ -98,8 +99,9 @@ const AdminSettings = () => {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (roles?.[selectedRole]?.permissions?.[PermissionTypes.AGENTS]) {
|
||||
reset(roles[selectedRole].permissions[PermissionTypes.AGENTS]);
|
||||
const value = roles?.[selectedRole]?.permissions?.[PermissionTypes.AGENTS];
|
||||
if (value) {
|
||||
reset(value);
|
||||
} else {
|
||||
reset(roleDefaults[selectedRole].permissions[PermissionTypes.AGENTS]);
|
||||
}
|
||||
|
|
@ -211,7 +213,8 @@ const AdminSettings = () => {
|
|||
</div>
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
type="button"
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
disabled={isSubmitting || isLoading}
|
||||
className="btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@ import { useWatch, useFormContext } from 'react-hook-form';
|
|||
import {
|
||||
SystemRoles,
|
||||
Permissions,
|
||||
ResourceType,
|
||||
PermissionTypes,
|
||||
PERMISSION_BITS,
|
||||
PermissionBits,
|
||||
} from 'librechat-data-provider';
|
||||
import type { AgentForm, AgentPanelProps } from '~/common';
|
||||
import { useLocalize, useAuthContext, useHasAccess, useResourcePermissions } from '~/hooks';
|
||||
|
|
@ -46,8 +47,8 @@ export default function AgentFooter({
|
|||
agent?._id || '',
|
||||
);
|
||||
|
||||
const canShareThisAgent = hasPermission(PERMISSION_BITS.SHARE);
|
||||
const canDeleteThisAgent = hasPermission(PERMISSION_BITS.DELETE);
|
||||
const canShareThisAgent = hasPermission(PermissionBits.SHARE);
|
||||
const canDeleteThisAgent = hasPermission(PermissionBits.DELETE);
|
||||
const renderSaveButton = () => {
|
||||
if (createMutation.isLoading || updateMutation.isLoading) {
|
||||
return <Spinner className="icon-md" aria-hidden="true" />;
|
||||
|
|
@ -84,7 +85,7 @@ export default function AgentFooter({
|
|||
resourceDbId={agent?._id}
|
||||
resourceId={agent_id}
|
||||
resourceName={agent?.name ?? ''}
|
||||
resourceType="agent"
|
||||
resourceType={ResourceType.AGENT}
|
||||
/>
|
||||
)}
|
||||
{agent && agent.author === user?.id && <DuplicateAgent agent_id={agent_id} />}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
Constants,
|
||||
SystemRoles,
|
||||
EModelEndpoint,
|
||||
PERMISSION_BITS,
|
||||
PermissionBits,
|
||||
isAssistantsEndpoint,
|
||||
} from 'librechat-data-provider';
|
||||
import type { AgentForm, StringOption } from '~/common';
|
||||
|
|
@ -57,7 +57,7 @@ export default function AgentPanel() {
|
|||
basicAgentQuery.data?._id || '',
|
||||
);
|
||||
|
||||
const canEdit = hasPermission(PERMISSION_BITS.EDIT);
|
||||
const canEdit = hasPermission(PermissionBits.EDIT);
|
||||
|
||||
const expandedAgentQuery = useGetExpandedAgentByIdQuery(current_agent_id ?? '', {
|
||||
enabled:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { Controller, useFormContext } from 'react-hook-form';
|
|||
import type { TSpecialVarLabel } from 'librechat-data-provider';
|
||||
import type { AgentForm } from '~/common';
|
||||
import { cn, defaultTextProps, removeFocusOutlines } from '~/utils';
|
||||
// import { ControlCombobox } from '@librechat/client';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
const inputClass = cn(
|
||||
|
|
@ -49,26 +48,6 @@ export default function Instructions() {
|
|||
{localize('com_ui_instructions')}
|
||||
</label>
|
||||
<div className="ml-auto" title="Add variables to instructions">
|
||||
{/* ControlCombobox implementation
|
||||
<ControlCombobox
|
||||
selectedValue=""
|
||||
displayValue="Add variables"
|
||||
items={variableOptions.map((option) => ({
|
||||
label: option.label,
|
||||
value: option.value,
|
||||
}))}
|
||||
setValue={handleAddVariable}
|
||||
ariaLabel="Add variable to instructions"
|
||||
searchPlaceholder="Search variables"
|
||||
selectPlaceholder="Add"
|
||||
isCollapsed={false}
|
||||
SelectIcon={<PlusCircle className="h-3 w-3 text-text-secondary" />}
|
||||
containerClassName="w-fit"
|
||||
className="h-7 gap-1 rounded-md border border-border-medium bg-surface-secondary px-2 py-0 text-sm text-text-primary transition-colors duration-200 hover:bg-surface-tertiary"
|
||||
iconSide="left"
|
||||
showCarat={false}
|
||||
/>
|
||||
*/}
|
||||
<DropdownPopup
|
||||
portal={true}
|
||||
mountByState={true}
|
||||
|
|
|
|||
|
|
@ -1,59 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Globe } from 'lucide-react';
|
||||
import { Switch } from '@librechat/client';
|
||||
import AccessRolesPicker from './AccessRolesPicker';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
interface PublicSharingToggleProps {
|
||||
isPublic: boolean;
|
||||
publicRole: string;
|
||||
onPublicToggle: (isPublic: boolean) => void;
|
||||
onPublicRoleChange: (role: string) => void;
|
||||
className?: string;
|
||||
resourceType?: string;
|
||||
}
|
||||
|
||||
export default function PublicSharingToggle({
|
||||
isPublic,
|
||||
publicRole,
|
||||
onPublicToggle,
|
||||
onPublicRoleChange,
|
||||
className = '',
|
||||
resourceType = 'agent',
|
||||
}: PublicSharingToggleProps) {
|
||||
const localize = useLocalize();
|
||||
|
||||
return (
|
||||
<div className={`space-y-3 border-t pt-4 ${className}`}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="flex items-center gap-2 text-sm font-medium">
|
||||
<Globe className="h-4 w-4" />
|
||||
{localize('com_ui_share_with_everyone')}
|
||||
</h3>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{localize('com_ui_make_agent_available_all_users')}
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={isPublic}
|
||||
onCheckedChange={onPublicToggle}
|
||||
aria-label={localize('com_ui_share_with_everyone')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isPublic && (
|
||||
<div>
|
||||
<label className="mb-2 block text-sm font-medium">
|
||||
{localize('com_ui_public_access_level')}
|
||||
</label>
|
||||
<AccessRolesPicker
|
||||
resourceType={resourceType}
|
||||
selectedRoleId={publicRole}
|
||||
onRoleChange={onPublicRoleChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { SystemRoles } from 'librechat-data-provider';
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import type { Agent, AgentCreateParams, TUser } from 'librechat-data-provider';
|
||||
import type { Agent, AgentCreateParams, TUser, ResourceType } from 'librechat-data-provider';
|
||||
import AgentFooter from '../AgentFooter';
|
||||
import { Panel } from '~/common';
|
||||
|
||||
|
|
@ -171,7 +171,7 @@ jest.mock('~/components/Sharing', () => ({
|
|||
resourceDbId: string;
|
||||
resourceId: string;
|
||||
resourceName: string;
|
||||
resourceType: string;
|
||||
resourceType: ResourceType;
|
||||
}) => (
|
||||
<div
|
||||
data-testid="grant-access-dialog"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
export * from './ui';
|
||||
export * from './Plugins';
|
||||
export * from './svg';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { dataService, MutationKeys, PERMISSION_BITS, QueryKeys } from 'librechat-data-provider';
|
||||
import { dataService, MutationKeys, PermissionBits, QueryKeys } from 'librechat-data-provider';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { QueryClient, UseMutationResult } from '@tanstack/react-query';
|
||||
|
||||
|
|
@ -7,8 +7,8 @@ import type { QueryClient, UseMutationResult } from '@tanstack/react-query';
|
|||
* AGENTS
|
||||
*/
|
||||
export const allAgentViewAndEditQueryKeys: t.AgentListParams[] = [
|
||||
{ requiredPermission: PERMISSION_BITS.VIEW },
|
||||
{ requiredPermission: PERMISSION_BITS.EDIT },
|
||||
{ requiredPermission: PermissionBits.VIEW },
|
||||
{ requiredPermission: PermissionBits.EDIT },
|
||||
];
|
||||
/**
|
||||
* Create a new agent
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
dataService,
|
||||
Permissions,
|
||||
EModelEndpoint,
|
||||
PERMISSION_BITS,
|
||||
PermissionBits,
|
||||
PermissionTypes,
|
||||
} from 'librechat-data-provider';
|
||||
import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
|
@ -26,7 +26,7 @@ export const useAgentListingDefaultPermissionLevel = () => {
|
|||
|
||||
// When marketplace is active: EDIT permissions (builder mode)
|
||||
// When marketplace is not active: VIEW permissions (browse mode)
|
||||
return hasMarketplaceAccess ? PERMISSION_BITS.EDIT : PERMISSION_BITS.VIEW;
|
||||
return hasMarketplaceAccess ? PermissionBits.EDIT : PermissionBits.VIEW;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -34,7 +34,7 @@ export const useAgentListingDefaultPermissionLevel = () => {
|
|||
*/
|
||||
export const defaultAgentParams: t.AgentListParams = {
|
||||
limit: 10,
|
||||
requiredPermission: PERMISSION_BITS.EDIT,
|
||||
requiredPermission: PermissionBits.EDIT,
|
||||
};
|
||||
/**
|
||||
* Hook for getting all available tools for A
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
isAgentsEndpoint,
|
||||
getConfigDefaults,
|
||||
isAssistantsEndpoint,
|
||||
PERMISSION_BITS,
|
||||
PermissionBits,
|
||||
} from 'librechat-data-provider';
|
||||
import type { TAssistantsMap, TEndpointsConfig } from 'librechat-data-provider';
|
||||
import type { MentionOption } from '~/common';
|
||||
|
|
@ -81,7 +81,7 @@ export default function useMentions({
|
|||
[startupConfig?.interface],
|
||||
);
|
||||
const { data: agentsList = null } = useListAgentsQuery(
|
||||
{ requiredPermission: PERMISSION_BITS.VIEW },
|
||||
{ requiredPermission: PermissionBits.VIEW },
|
||||
{
|
||||
enabled: hasAgentAccess && interfaceConfig.modelSelect === true,
|
||||
select: (res) => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
isAgentsEndpoint,
|
||||
tQueryParamsSchema,
|
||||
isAssistantsEndpoint,
|
||||
PERMISSION_BITS,
|
||||
PermissionBits,
|
||||
} from 'librechat-data-provider';
|
||||
import type {
|
||||
TPreset,
|
||||
|
|
@ -80,7 +80,7 @@ const processValidSettings = (queryParams: Record<string, string>) => {
|
|||
};
|
||||
|
||||
const injectAgentIntoAgentsMap = (queryClient: QueryClient, agent: any) => {
|
||||
const editCacheKey = [QueryKeys.agents, { requiredPermission: PERMISSION_BITS.EDIT }];
|
||||
const editCacheKey = [QueryKeys.agents, { requiredPermission: PermissionBits.EDIT }];
|
||||
const editCache = queryClient.getQueryData<AgentListResponse>(editCacheKey);
|
||||
|
||||
if (editCache?.data && !editCache.data.some((cachedAgent) => cachedAgent.id === agent.id)) {
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@ import {
|
|||
useGetResourcePermissionsQuery,
|
||||
useUpdateResourcePermissionsMutation,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import type { TPrincipal } from 'librechat-data-provider';
|
||||
import type { TPrincipal, ResourceType, AccessRoleIds } from 'librechat-data-provider';
|
||||
import { getResourceConfig } from '~/utils';
|
||||
|
||||
/**
|
||||
* Hook to manage resource permission state including current shares, public access, and mutations
|
||||
* @param resourceType - Type of resource (e.g., 'agent', 'promptGroup')
|
||||
* @param resourceType - Type of resource (e.g., ResourceType.AGENT, ResourceType.PROMPTGROUP)
|
||||
* @param resourceDbId - Database ID of the resource
|
||||
* @param isModalOpen - Whether the modal is open (for effect dependencies)
|
||||
* @returns Object with permission state and update mutation
|
||||
*/
|
||||
export const useResourcePermissionState = (
|
||||
resourceType: string,
|
||||
resourceType: ResourceType,
|
||||
resourceDbId: string | null | undefined,
|
||||
isModalOpen: boolean = false,
|
||||
) => {
|
||||
|
|
@ -52,13 +52,15 @@ export const useResourcePermissionState = (
|
|||
|
||||
// State for managing public access
|
||||
const [isPublic, setIsPublic] = useState(false);
|
||||
const [publicRole, setPublicRole] = useState<string>(config?.defaultViewerRoleId ?? '');
|
||||
const [publicRole, setPublicRole] = useState<AccessRoleIds | undefined>(
|
||||
config?.defaultViewerRoleId,
|
||||
);
|
||||
|
||||
// Sync state with permissions data when modal opens
|
||||
useEffect(() => {
|
||||
if (permissionsData && isModalOpen) {
|
||||
setIsPublic(currentIsPublic ?? false);
|
||||
setPublicRole(currentPublicRole ?? '');
|
||||
setPublicRole(currentPublicRole);
|
||||
}
|
||||
}, [permissionsData, isModalOpen, currentIsPublic, currentPublicRole]);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export * from './Messages';
|
|||
export * from './Plugins';
|
||||
export * from './Prompts';
|
||||
export * from './Roles';
|
||||
export * from './Sharing';
|
||||
export * from './SSE';
|
||||
export * from './AuthContext';
|
||||
export * from './ScreenshotContext';
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
import {
|
||||
useGetEffectivePermissionsQuery,
|
||||
hasPermissions,
|
||||
useGetEffectivePermissionsQuery,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import type { ResourceType } from 'librechat-data-provider';
|
||||
|
||||
/**
|
||||
* fetches resource permissions once and returns a function to check any permission
|
||||
* More efficient when checking multiple permissions for the same resource
|
||||
* @param resourceType - Type of resource (e.g., 'agent')
|
||||
* @param resourceType - Type of resource (e.g., ResourceType.AGENT)
|
||||
* @param resourceId - ID of the resource
|
||||
* @returns Object with hasPermission function and loading state
|
||||
*/
|
||||
export const useResourcePermissions = (resourceType: string, resourceId: string) => {
|
||||
export const useResourcePermissions = (resourceType: ResourceType, resourceId: string) => {
|
||||
const { data, isLoading } = useGetEffectivePermissionsQuery(resourceType, resourceId);
|
||||
|
||||
const hasPermission = (requiredPermission: number): boolean => {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
TwoFactorScreen,
|
||||
RequestPasswordReset,
|
||||
} from '~/components/Auth';
|
||||
import AgentMarketplace from '~/components/Agents/Marketplace';
|
||||
import { OAuthSuccess, OAuthError } from '~/components/OAuth';
|
||||
import { AuthContextProvider } from '~/hooks/AuthContext';
|
||||
import RouteErrorBoundary from './RouteErrorBoundary';
|
||||
|
|
@ -18,7 +19,6 @@ import ShareRoute from './ShareRoute';
|
|||
import ChatRoute from './ChatRoute';
|
||||
import Search from './Search';
|
||||
import Root from './Root';
|
||||
import AgentMarketplace from '~/components/SidePanel/Agents/AgentMarketplace';
|
||||
|
||||
const AuthLayout = () => (
|
||||
<AuthContextProvider>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { ACCESS_ROLE_IDS } from 'librechat-data-provider';
|
||||
import { AccessRoleIds, ResourceType } from 'librechat-data-provider';
|
||||
|
||||
export interface ResourceConfig {
|
||||
resourceType: string;
|
||||
defaultViewerRoleId: string;
|
||||
defaultEditorRoleId: string;
|
||||
defaultOwnerRoleId: string;
|
||||
resourceType: ResourceType;
|
||||
defaultViewerRoleId: AccessRoleIds;
|
||||
defaultEditorRoleId: AccessRoleIds;
|
||||
defaultOwnerRoleId: AccessRoleIds;
|
||||
getResourceUrl?: (resourceId: string) => string;
|
||||
getResourceName: (resourceName?: string) => string;
|
||||
getShareMessage: (resourceName?: string) => string;
|
||||
|
|
@ -12,12 +12,12 @@ export interface ResourceConfig {
|
|||
getCopyUrlMessage: () => string;
|
||||
}
|
||||
|
||||
export const RESOURCE_CONFIGS: Record<string, ResourceConfig> = {
|
||||
agent: {
|
||||
resourceType: 'agent',
|
||||
defaultViewerRoleId: ACCESS_ROLE_IDS.AGENT_VIEWER,
|
||||
defaultEditorRoleId: ACCESS_ROLE_IDS.AGENT_EDITOR,
|
||||
defaultOwnerRoleId: ACCESS_ROLE_IDS.AGENT_OWNER,
|
||||
export const RESOURCE_CONFIGS: Record<ResourceType, ResourceConfig> = {
|
||||
[ResourceType.AGENT]: {
|
||||
resourceType: ResourceType.AGENT,
|
||||
defaultViewerRoleId: AccessRoleIds.AGENT_VIEWER,
|
||||
defaultEditorRoleId: AccessRoleIds.AGENT_EDITOR,
|
||||
defaultOwnerRoleId: AccessRoleIds.AGENT_OWNER,
|
||||
getResourceUrl: (agentId: string) => `${window.location.origin}/c/new?agent_id=${agentId}`,
|
||||
getResourceName: (name?: string) => (name && name !== '' ? `"${name}"` : 'agent'),
|
||||
getShareMessage: (name?: string) => (name && name !== '' ? `"${name}"` : 'agent'),
|
||||
|
|
@ -25,11 +25,11 @@ export const RESOURCE_CONFIGS: Record<string, ResourceConfig> = {
|
|||
`Manage permissions for ${name && name !== '' ? `"${name}"` : 'agent'}`,
|
||||
getCopyUrlMessage: () => 'Agent URL copied',
|
||||
},
|
||||
promptGroup: {
|
||||
resourceType: 'promptGroup',
|
||||
defaultViewerRoleId: ACCESS_ROLE_IDS.PROMPTGROUP_VIEWER,
|
||||
defaultEditorRoleId: ACCESS_ROLE_IDS.PROMPTGROUP_EDITOR,
|
||||
defaultOwnerRoleId: ACCESS_ROLE_IDS.PROMPTGROUP_OWNER,
|
||||
[ResourceType.PROMPTGROUP]: {
|
||||
resourceType: ResourceType.PROMPTGROUP,
|
||||
defaultViewerRoleId: AccessRoleIds.PROMPTGROUP_VIEWER,
|
||||
defaultEditorRoleId: AccessRoleIds.PROMPTGROUP_EDITOR,
|
||||
defaultOwnerRoleId: AccessRoleIds.PROMPTGROUP_OWNER,
|
||||
getResourceName: (name?: string) => (name && name !== '' ? `"${name}"` : 'prompt'),
|
||||
getShareMessage: (name?: string) => (name && name !== '' ? `"${name}"` : 'prompt'),
|
||||
getManageMessage: (name?: string) =>
|
||||
|
|
@ -38,6 +38,6 @@ export const RESOURCE_CONFIGS: Record<string, ResourceConfig> = {
|
|||
},
|
||||
};
|
||||
|
||||
export const getResourceConfig = (resourceType: string): ResourceConfig | undefined => {
|
||||
export const getResourceConfig = (resourceType: ResourceType): ResourceConfig | undefined => {
|
||||
return RESOURCE_CONFIGS[resourceType];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { ACCESS_ROLE_IDS } from 'librechat-data-provider';
|
||||
import type { AccessRoleIds } from 'librechat-data-provider';
|
||||
import type { TranslationKeys } from '~/hooks/useLocalize';
|
||||
|
||||
/**
|
||||
|
|
@ -43,7 +43,7 @@ export const ROLE_LOCALIZATIONS = {
|
|||
* @returns Object with name and description localization keys, or unknown keys if not found
|
||||
*/
|
||||
export const getRoleLocalizationKeys = (
|
||||
roleId: ACCESS_ROLE_IDS,
|
||||
roleId: AccessRoleIds,
|
||||
): {
|
||||
name: TranslationKeys;
|
||||
description: TranslationKeys;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue