sprint4+5: field-level permissions complete + universal ShareDialog frontend
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* React Query hooks for the universal entity permission API.
|
||||
*/
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
fetchEntityPermissions,
|
||||
createEntityPermission,
|
||||
updateEntityPermission,
|
||||
deleteEntityPermission,
|
||||
fetchEntityAccess,
|
||||
fetchEntityRegistry,
|
||||
type CreateEntityPermissionPayload,
|
||||
type UpdateEntityPermissionPayload,
|
||||
} from './entityPermissions';
|
||||
|
||||
// ─── Query Key Factory ─────────────────────────────────────────────────────
|
||||
|
||||
export const entityPermissionKeys = {
|
||||
all: ['entityPermissions'] as const,
|
||||
list: (entityType: string, entityId: string) =>
|
||||
[...entityPermissionKeys.all, entityType, entityId] as const,
|
||||
access: (entityType: string, entityId: string) =>
|
||||
['entityAccess', entityType, entityId] as const,
|
||||
registry: () => ['entityRegistry'] as const,
|
||||
};
|
||||
|
||||
// ─── Hooks ─────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Fetch all permissions for a given entity.
|
||||
*/
|
||||
export function useEntityPermissions(entityType: string, entityId: string) {
|
||||
return useQuery({
|
||||
queryKey: entityPermissionKeys.list(entityType, entityId),
|
||||
queryFn: () => fetchEntityPermissions(entityType, entityId),
|
||||
enabled: !!entityType && !!entityId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new permission on an entity.
|
||||
*/
|
||||
export function useCreateEntityPermission() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
entityType,
|
||||
entityId,
|
||||
data,
|
||||
}: {
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
data: CreateEntityPermissionPayload;
|
||||
}) => createEntityPermission(entityType, entityId, data),
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: entityPermissionKeys.list(variables.entityType, variables.entityId),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing permission on an entity.
|
||||
*/
|
||||
export function useUpdateEntityPermission() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
entityType,
|
||||
entityId,
|
||||
permId,
|
||||
data,
|
||||
}: {
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
permId: string;
|
||||
data: UpdateEntityPermissionPayload;
|
||||
}) => updateEntityPermission(entityType, entityId, permId, data),
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: entityPermissionKeys.list(variables.entityType, variables.entityId),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a permission from an entity.
|
||||
*/
|
||||
export function useDeleteEntityPermission() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
entityType,
|
||||
entityId,
|
||||
permId,
|
||||
}: {
|
||||
entityType: string;
|
||||
entityId: string;
|
||||
permId: string;
|
||||
}) => deleteEntityPermission(entityType, entityId, permId),
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: entityPermissionKeys.list(variables.entityType, variables.entityId),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch access info for the current user on an entity.
|
||||
*/
|
||||
export function useEntityAccess(entityType: string, entityId: string) {
|
||||
return useQuery({
|
||||
queryKey: entityPermissionKeys.access(entityType, entityId),
|
||||
queryFn: () => fetchEntityAccess(entityType, entityId),
|
||||
enabled: !!entityType && !!entityId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the registry of all supported entity types.
|
||||
*/
|
||||
export function useEntityRegistry() {
|
||||
return useQuery({
|
||||
queryKey: entityPermissionKeys.registry(),
|
||||
queryFn: () => fetchEntityRegistry(),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Universal Entity Permission API client.
|
||||
*
|
||||
* Works with any entity type via the generic permissions endpoint:
|
||||
* /api/v1/permissions/{entity_type}/{entity_id}
|
||||
*/
|
||||
|
||||
import { apiDelete, apiGet, apiPost, apiPut } from './client';
|
||||
|
||||
// ─── Types ─────────────────────────────────────────────────────────────────
|
||||
|
||||
export type PrincipalType = 'user' | 'group';
|
||||
|
||||
export type PermissionLevel = 'read' | 'write' | 'admin' | 'delete';
|
||||
|
||||
export interface EntityPermission {
|
||||
id: string;
|
||||
entity_type: string;
|
||||
entity_id: string;
|
||||
user_id: string | null;
|
||||
group_id: string | null;
|
||||
principal_type: PrincipalType;
|
||||
user_name: string | null;
|
||||
group_name: string | null;
|
||||
user_email: string | null;
|
||||
permission_level: PermissionLevel;
|
||||
expires_at: string | null;
|
||||
created_at: string | null;
|
||||
updated_at: string | null;
|
||||
}
|
||||
|
||||
export interface EntityAccessInfo {
|
||||
entity_type: string;
|
||||
entity_id: string;
|
||||
access_level: PermissionLevel | 'owner' | 'none';
|
||||
is_owner: boolean;
|
||||
is_shared: boolean;
|
||||
owner_name: string | null;
|
||||
owner_email: string | null;
|
||||
}
|
||||
|
||||
export interface EntityRegistryEntry {
|
||||
entity_type: string;
|
||||
display_name: string;
|
||||
description: string | null;
|
||||
}
|
||||
|
||||
export interface EntityPermissionListResponse {
|
||||
items: EntityPermission[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface CreateEntityPermissionPayload {
|
||||
user_id?: string;
|
||||
group_id?: string;
|
||||
permission_level: PermissionLevel;
|
||||
expires_at?: string | null;
|
||||
}
|
||||
|
||||
export interface UpdateEntityPermissionPayload {
|
||||
permission_level?: PermissionLevel;
|
||||
expires_at?: string | null;
|
||||
}
|
||||
|
||||
// ─── API Functions ─────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Fetch all permissions for a given entity.
|
||||
*/
|
||||
export function fetchEntityPermissions(
|
||||
entityType: string,
|
||||
entityId: string
|
||||
): Promise<EntityPermissionListResponse> {
|
||||
return apiGet<EntityPermissionListResponse>(
|
||||
`/permissions/${entityType}/${entityId}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new permission on an entity.
|
||||
*/
|
||||
export function createEntityPermission(
|
||||
entityType: string,
|
||||
entityId: string,
|
||||
data: CreateEntityPermissionPayload
|
||||
): Promise<EntityPermission> {
|
||||
return apiPost<EntityPermission>(
|
||||
`/permissions/${entityType}/${entityId}`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing permission on an entity.
|
||||
*/
|
||||
export function updateEntityPermission(
|
||||
entityType: string,
|
||||
entityId: string,
|
||||
permId: string,
|
||||
data: UpdateEntityPermissionPayload
|
||||
): Promise<EntityPermission> {
|
||||
return apiPut<EntityPermission>(
|
||||
`/permissions/${entityType}/${entityId}/${permId}`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a permission from an entity.
|
||||
*/
|
||||
export function deleteEntityPermission(
|
||||
entityType: string,
|
||||
entityId: string,
|
||||
permId: string
|
||||
): Promise<void> {
|
||||
return apiDelete<void>(
|
||||
`/permissions/${entityType}/${entityId}/${permId}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch access info for the current user on an entity.
|
||||
*/
|
||||
export function fetchEntityAccess(
|
||||
entityType: string,
|
||||
entityId: string
|
||||
): Promise<EntityAccessInfo> {
|
||||
return apiGet<EntityAccessInfo>(
|
||||
`/permissions/${entityType}/${entityId}/access`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the registry of all supported entity types.
|
||||
*/
|
||||
export function fetchEntityRegistry(): Promise<EntityRegistryEntry[]> {
|
||||
return apiGet<EntityRegistryEntry[]>('/permissions/registry');
|
||||
}
|
||||
Reference in New Issue
Block a user