From c54549d2382dc923e68cae1a7aa10725bd939b57 Mon Sep 17 00:00:00 2001 From: Babayaga Date: Sun, 5 Apr 2026 10:34:09 +0200 Subject: [PATCH] acl ui - 1/2 --- .../ui/src/components/admin/AclEditor.tsx | 61 -- .../ui/src/components/admin/GroupManager.tsx | 28 +- .../src/components/admin/PermissionPicker.tsx | 8 +- .../src/components/admin/StorageManager.tsx | 2 +- .../components/widgets/CategoryManager.tsx | 2 +- packages/ui/src/modules/acl/ACLBaseEditor.tsx | 712 +++++------------- packages/ui/src/modules/acl/client-acl.ts | 6 + packages/ui/src/modules/storage/AclEditor.tsx | 126 ++++ 8 files changed, 335 insertions(+), 610 deletions(-) delete mode 100644 packages/ui/src/components/admin/AclEditor.tsx create mode 100644 packages/ui/src/modules/storage/AclEditor.tsx diff --git a/packages/ui/src/components/admin/AclEditor.tsx b/packages/ui/src/components/admin/AclEditor.tsx deleted file mode 100644 index 6f838c73..00000000 --- a/packages/ui/src/components/admin/AclEditor.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { useCallback } from "react"; -import { ACLBaseEditor, type AclGrantInput, type AclRevokeInput } from "@/modules/acl/ACLBaseEditor"; -import { fetchAclSettings, grantAclPermission, revokeAclPermission } from "@/modules/acl/client-acl"; -import { fetchProfilesByUserIds } from "@/modules/user/client-user"; - -interface AclEditorProps { - /** Resource type — e.g. 'vfs', 'layout', 'page' */ - resourceType?: string; - mount: string; - path: string; - /** Compact mode for narrow containers (e.g. category manager sidebar) */ - compact?: boolean; -} - -/** VFS-oriented ACL editor: `resourceId` is the mount name. For other backends, use {@link ACLBaseEditor}. */ -export function AclEditor({ resourceType = "vfs", mount, path, compact = false }: AclEditorProps) { - const loadEntries = useCallback(async () => { - const settings = await fetchAclSettings(resourceType, mount); - return settings.acl || []; - }, [resourceType, mount]); - - const grant = useCallback( - async (input: AclGrantInput) => { - await grantAclPermission(resourceType, mount, input); - }, - [resourceType, mount], - ); - - const revoke = useCallback( - async (input: AclRevokeInput) => { - await revokeAclPermission(resourceType, mount, input); - }, - [resourceType, mount], - ); - - return ( - - - {mount}:{path} - - - } - resourceContextNote={<> (Mount: {mount})} - loadEntries={loadEntries} - grant={grant} - revoke={revoke} - fetchProfiles={fetchProfilesByUserIds} - /> - ); -} - -export { - ACLBaseEditor, - ANONYMOUS_USER_ID, - AUTHENTICATED_USER_ID, -} from "@/modules/acl/ACLBaseEditor"; -export type { ACLBaseEditorProps, AclGrantInput, AclRevokeInput } from "@/modules/acl/ACLBaseEditor"; diff --git a/packages/ui/src/components/admin/GroupManager.tsx b/packages/ui/src/components/admin/GroupManager.tsx index 0da1f8a1..9825ad4e 100644 --- a/packages/ui/src/components/admin/GroupManager.tsx +++ b/packages/ui/src/components/admin/GroupManager.tsx @@ -25,8 +25,7 @@ import { Loader2, UserMinus } from "lucide-react"; -import { AclEditor } from "@/components/admin/AclEditor"; -import CollapsibleSection from "@/components/CollapsibleSection"; + import { cn } from "@/lib/utils"; import { T, translate } from "@/i18n"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; @@ -358,30 +357,7 @@ export const GroupManager = () => { - {/* Permissions Section */} - - - Permissions & ACLs - - } - initiallyOpen={false} - minimal - className="bg-muted/5 rounded-xl border p-2" - > -
-
- These permissions apply to resources where this group is explicitly granted access. -
- -
-
+ ) : ( diff --git a/packages/ui/src/components/admin/PermissionPicker.tsx b/packages/ui/src/components/admin/PermissionPicker.tsx index b2898bfe..24b06330 100644 --- a/packages/ui/src/components/admin/PermissionPicker.tsx +++ b/packages/ui/src/components/admin/PermissionPicker.tsx @@ -7,8 +7,6 @@ import React from "react"; import { Checkbox } from "@/components/ui/checkbox"; -const ALL_PERMISSIONS = ['read', 'list', 'write', 'delete'] as const; - interface PermissionPickerProps { /** Currently selected permissions */ value: string[]; @@ -16,9 +14,11 @@ interface PermissionPickerProps { onChange: (perms: string[]) => void; /** Disable interaction */ disabled?: boolean; + /** List of available permissions to pick from */ + availablePermissions: readonly string[]; } -export const PermissionPicker: React.FC = ({ value, onChange, disabled }) => { +export const PermissionPicker: React.FC = ({ value, onChange, disabled, availablePermissions }) => { const toggle = (perm: string, checked: boolean) => { const next = checked ? [...value.filter(p => p !== perm), perm] @@ -28,7 +28,7 @@ export const PermissionPicker: React.FC = ({ value, onCha return (
- {ALL_PERMISSIONS.map(p => ( + {availablePermissions.map(p => (