Add horizontal limit, search bar and change icons of access tree component

This commit is contained in:
carlosmonastyrski
2025-03-26 11:13:12 -03:00
parent bc1b7ddcc5
commit d5741b4a72
9 changed files with 339 additions and 43 deletions
@@ -5,6 +5,7 @@ import { Edge, Node, useEdgesState, useNodesState } from "@xyflow/react";
import { ProjectPermissionSub, useWorkspace } from "@app/context";
import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext";
import { useListProjectEnvironmentsFolders } from "@app/hooks/api/secretFolders/queries";
import { TSecretFolderWithPath } from "@app/hooks/api/secretFolders/types";
import { useAccessTreeContext } from "../components";
import { PermissionAccess } from "../types";
@@ -15,8 +16,24 @@ import {
getSubjectActionRuleMap,
positionElements
} from "../utils";
import { createShowMoreNode } from "../utils/createShowMoreNode";
export const useAccessTree = (permissions: MongoAbility<ProjectPermissionSet, MongoQuery>) => {
const INITIAL_FOLDERS_PER_LEVEL = 10;
const FOLDERS_INCREMENT = 10;
type LevelFolderMap = Record<
string,
{
folders: TSecretFolderWithPath[];
visibleCount: number;
hasMore: boolean;
}
>;
export const useAccessTree = (
permissions: MongoAbility<ProjectPermissionSet, MongoQuery>,
searchPath: string
) => {
const { currentWorkspace } = useWorkspace();
const { secretName, setSecretName, setViewMode, viewMode } = useAccessTreeContext();
const [nodes, setNodes] = useNodesState<Node>([]);
@@ -27,10 +44,104 @@ export const useAccessTree = (permissions: MongoAbility<ProjectPermissionSet, Mo
currentWorkspace.id
);
const [levelFolderMap, setLevelFolderMap] = useState<LevelFolderMap>({});
const [totalFolderCount, setTotalFolderCount] = useState(0);
const showMoreFolders = (parentId: string) => {
setLevelFolderMap((prevMap) => {
const level = prevMap[parentId];
if (!level) return prevMap;
const newVisibleCount = Math.min(
level.visibleCount + FOLDERS_INCREMENT,
level.folders.length
);
return {
...prevMap,
[parentId]: {
...level,
visibleCount: newVisibleCount,
hasMore: newVisibleCount < level.folders.length
}
};
});
};
const levelsWithMoreFolders = Object.entries(levelFolderMap)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.filter(([_, level]) => level.hasMore)
.map(([parentId]) => parentId);
const getLevelCounts = (parentId: string) => {
const level = levelFolderMap[parentId];
if (!level) return { visibleCount: 0, totalCount: 0, hasMore: false };
return {
visibleCount: level.visibleCount,
totalCount: level.folders.length,
hasMore: level.hasMore
};
};
useEffect(() => {
if (!environmentsFolders || !permissions || !environmentsFolders[environment]) return;
const { folders, name } = environmentsFolders[environment];
const { folders } = environmentsFolders[environment];
setTotalFolderCount(folders.length);
const groupedFolders: Record<string, TSecretFolderWithPath[]> = {};
const filteredFolders = folders.filter((folder) => {
if (folder.path.startsWith(searchPath)) {
return true;
}
if (
searchPath.startsWith(folder.path) &&
(folder.path === "/" ||
searchPath === folder.path ||
searchPath.indexOf("/", folder.path.length) === folder.path.length)
) {
return true;
}
return false;
});
filteredFolders.forEach((folder) => {
const parentId = folder.parentId || "";
if (!groupedFolders[parentId]) {
groupedFolders[parentId] = [];
}
groupedFolders[parentId].push(folder);
});
const newLevelFolderMap: LevelFolderMap = {};
Object.entries(groupedFolders).forEach(([parentId, folderList]) => {
const key = parentId;
newLevelFolderMap[key] = {
folders: folderList,
visibleCount: Math.min(INITIAL_FOLDERS_PER_LEVEL, folderList.length),
hasMore: folderList.length > INITIAL_FOLDERS_PER_LEVEL
};
});
setLevelFolderMap(newLevelFolderMap);
}, [permissions, environmentsFolders, environment, subject, secretName, searchPath]);
useEffect(() => {
if (
!environmentsFolders ||
!permissions ||
!environmentsFolders[environment] ||
Object.keys(levelFolderMap).length === 0
)
return;
const { name } = environmentsFolders[environment];
const roleNode = createRoleNode({
subject,
@@ -39,7 +150,13 @@ export const useAccessTree = (permissions: MongoAbility<ProjectPermissionSet, Mo
const actionRuleMap = getSubjectActionRuleMap(subject, permissions);
const folderNodes = folders.map((folder) =>
const visibleFolders: TSecretFolderWithPath[] = [];
Object.values(levelFolderMap).forEach((levelData) => {
visibleFolders.push(...levelData.folders.slice(0, levelData.visibleCount));
});
const folderNodes = visibleFolders.map((folder) =>
createFolderNode({
folder,
permissions,
@@ -69,10 +186,44 @@ export const useAccessTree = (permissions: MongoAbility<ProjectPermissionSet, Mo
});
});
const init = positionElements([roleNode, ...folderNodes], [...folderEdges]);
const addMoreButtons: Node[] = [];
Object.entries(levelFolderMap).forEach(([parentId, levelData]) => {
const key = parentId === "null" ? null : parentId;
if (key && levelData.hasMore) {
const showMoreButtonNode = createShowMoreNode({
parentId: key,
onClick: () => showMoreFolders(key),
remaining: levelData.folders.length - levelData.visibleCount
});
addMoreButtons.push(showMoreButtonNode);
folderEdges.push(
createBaseEdge({
source: key,
target: showMoreButtonNode.id,
access: PermissionAccess.Full,
hideEdge: true
})
);
}
});
const init = positionElements([roleNode, ...folderNodes, ...addMoreButtons], [...folderEdges]);
setNodes(init.nodes);
setEdges(init.edges);
}, [permissions, environmentsFolders, environment, subject, secretName, setNodes, setEdges]);
}, [
levelFolderMap,
permissions,
environmentsFolders,
environment,
subject,
secretName,
setNodes,
setEdges
]);
return {
nodes,
@@ -86,6 +237,11 @@ export const useAccessTree = (permissions: MongoAbility<ProjectPermissionSet, Mo
secretName,
setSecretName,
viewMode,
setViewMode
setViewMode,
levelFolderMap,
showMoreFolders,
levelsWithMoreFolders,
getLevelCounts,
totalFolderCount
};
};