({
+ defaultValues: {
+ room: '',
+ attributes: [{ key: '', values: [] }],
+ ...defaultValues,
+ },
+ mode: 'onChange',
+ });
+
+ return {children};
+};
+
+describe('RoomFormAttributeFields', () => {
+ const mockRemove = jest.fn();
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render the correct number of fields', () => {
+ const fields = [{ id: 'field-1' }, { id: 'field-2' }, { id: 'field-3' }];
+
+ render(
+
+
+ ,
+ { wrapper: appRoot },
+ );
+
+ const attributeLabels = screen.getAllByText('Attribute');
+ expect(attributeLabels).toHaveLength(3);
+ });
+
+ it('should render a single field', () => {
+ const fields = [{ id: 'field-1' }];
+
+ render(
+
+
+ ,
+ { wrapper: appRoot },
+ );
+
+ const attributeLabels = screen.getAllByText('Attribute');
+ expect(attributeLabels).toHaveLength(1);
+ });
+
+ it('should render multiple fields', () => {
+ const fields = [{ id: 'field-1' }, { id: 'field-2' }, { id: 'field-3' }, { id: 'field-4' }, { id: 'field-5' }];
+
+ render(
+
+
+ ,
+ { wrapper: appRoot },
+ );
+
+ const attributeLabels = screen.getAllByText('Attribute');
+ expect(attributeLabels).toHaveLength(5);
+ });
+
+ it('should render fields with provided default values', () => {
+ const fields = [{ id: 'field-1' }, { id: 'field-2' }];
+
+ render(
+
+
+ ,
+ { wrapper: appRoot },
+ );
+
+ const attributeLabels = screen.getAllByText('Attribute');
+ expect(attributeLabels).toHaveLength(2);
+ expect(screen.getByText('Department')).toBeInTheDocument();
+ expect(screen.getByText('Engineering')).toBeInTheDocument();
+ expect(screen.getByText('Security-Level')).toBeInTheDocument();
+ expect(screen.getByText('Public')).toBeInTheDocument();
+ expect(screen.getByText('Internal')).toBeInTheDocument();
+ });
+});
diff --git a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomFormAttributeFields.tsx b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomFormAttributeFields.tsx
new file mode 100644
index 0000000000000..bfcdf21464b28
--- /dev/null
+++ b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomFormAttributeFields.tsx
@@ -0,0 +1,37 @@
+import { Field, FieldLabel, InputBoxSkeleton } from '@rocket.chat/fuselage';
+import { useTranslation } from 'react-i18next';
+
+import RoomFormAttributeField from './RoomFormAttributeField';
+import { useAttributeList } from '../hooks/useAttributeList';
+
+type RoomFormAttributeFieldsProps = {
+ fields: { id: string }[];
+ remove: (index: number) => void;
+};
+
+const RoomFormAttributeFields = ({ fields, remove }: RoomFormAttributeFieldsProps) => {
+ const { t } = useTranslation();
+
+ const { data: attributeList, isLoading } = useAttributeList();
+
+ if (isLoading || !attributeList) {
+ return ;
+ }
+
+ return fields.map((field, index) => (
+
+
+ {t('Attribute')}
+
+ {
+ remove(index);
+ }}
+ index={index}
+ />
+
+ ));
+};
+
+export default RoomFormAttributeFields;
diff --git a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/__snapshots__/RoomFormAttributeField.spec.tsx.snap b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/__snapshots__/RoomFormAttributeField.spec.tsx.snap
index de31c45c5e177..3495a43b53f61 100644
--- a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/__snapshots__/RoomFormAttributeField.spec.tsx.snap
+++ b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/__snapshots__/RoomFormAttributeField.spec.tsx.snap
@@ -17,10 +17,14 @@ exports[`RoomFormAttributeField renders Default without crashing 1`] = `
name="attributes.0.key"
>
+
+ ABAC_Search_Attribute
+
{
+const COUNT = 150;
+
+export const useAttributeList = () => {
const attributesAutoCompleteEndpoint = useEndpoint('GET', '/v1/abac/attributes');
const isABACAvailable = useIsABACAvailable();
- return useInfiniteQuery({
+ return useQuery({
enabled: isABACAvailable,
- queryKey: ABACQueryKeys.roomAttributes.list({ key: filter ?? '' }),
- queryFn: async ({ pageParam: offset = 0 }) => {
- // TODO: Check endpoint types
- const { attributes, ...data } = await attributesAutoCompleteEndpoint({ key: filter, offset, count: 15 });
+ queryKey: ABACQueryKeys.roomAttributes.list(),
+ queryFn: async () => {
+ const firstPage = await attributesAutoCompleteEndpoint({ offset: 0, count: COUNT });
+ const { attributes: firstPageAttributes, total } = firstPage;
+
+ let currentPage = COUNT;
+ const pages = [];
+
+ while (currentPage < total) {
+ pages.push(attributesAutoCompleteEndpoint({ offset: currentPage, count: COUNT }));
+ currentPage += COUNT;
+ }
+ const remainingPages = await Promise.all(pages);
return {
- ...data,
- attributes: attributes.map((attribute) => ({
+ attributes: [...firstPageAttributes, ...remainingPages.flatMap((page) => page.attributes)].map((attribute) => ({
_id: attribute._id,
label: attribute.key,
value: attribute.key,
@@ -25,15 +35,5 @@ export const useAttributeList = (filter?: string) => {
})),
};
},
- select: (data) => data.pages.flatMap((page) => page.attributes),
- initialPageParam: 0,
- getNextPageParam: (lastPage) => {
- const offset = lastPage.offset + lastPage.count;
- return offset < lastPage.total ? offset : undefined;
- },
- initialData: () => ({
- pages: [{ attributes: [], offset: 0, count: 0, total: Infinity }],
- pageParams: [0],
- }),
});
};
diff --git a/apps/meteor/ee/server/api/abac/schemas.ts b/apps/meteor/ee/server/api/abac/schemas.ts
index 85fea475f619c..0d679293fcc81 100644
--- a/apps/meteor/ee/server/api/abac/schemas.ts
+++ b/apps/meteor/ee/server/api/abac/schemas.ts
@@ -64,7 +64,7 @@ const GetAbacAttributesQuery = {
additionalProperties: false,
};
-export const GETAbacAttributesQuerySchema = ajv.compile<{ key?: string; values?: string; offset: number; count: number }>(
+export const GETAbacAttributesQuerySchema = ajv.compile<{ key?: string; values?: string; offset: number; count?: number }>(
GetAbacAttributesQuery,
);