From ffb9a058022baa346d5ca242a2a7129c0eae8b03 Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Thu, 11 Dec 2025 14:28:52 -0300 Subject: [PATCH 1/7] regression: truncate abac table cells --- .../views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx | 4 ++-- .../client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx index 24e3967fbb42e..0309c8a73df28 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesPage.tsx @@ -83,8 +83,8 @@ const AttributesPage = () => { {data?.attributes?.map((attribute) => ( - {attribute.key} - {attribute.values.join(', ')} + {attribute.key} + {attribute.values.join(', ')} diff --git a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx index c7a93d5d600e9..7864e5670c6c6 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACRoomsTab/RoomsPage.tsx @@ -106,8 +106,12 @@ const RoomsPage = () => { {room.fname || room.name} {room.usersCount} - {room.abacAttributes?.flatMap((attribute) => attribute.key ?? []).join(', ')} - {room.abacAttributes?.flatMap((attribute) => attribute.values ?? []).join(', ')} + + {room.abacAttributes?.flatMap((attribute) => attribute.key ?? []).join(', ')} + + + {room.abacAttributes?.flatMap((attribute) => attribute.values ?? []).join(', ')} + From b0cc02beb4a675c1037010dbf7919c6a1097afa6 Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Thu, 11 Dec 2025 16:13:16 -0300 Subject: [PATCH 2/7] regression: add validation for duplicated attribute values --- .../AttributesContextualBar.tsx | 1 + .../ABAC/ABACAttributesTab/AttributesForm.tsx | 76 +++++++++++++------ packages/i18n/src/locales/en.i18n.json | 1 + 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesContextualBar.tsx b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesContextualBar.tsx index 78990e95b6b83..c346c1ce5de64 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesContextualBar.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesContextualBar.tsx @@ -38,6 +38,7 @@ const AttributesContextualBar = ({ attributeData, onClose }: AttributesContextua attributeValues: [{ value: '' }], lockedAttributes: [], }, + mode: 'onChange', }); const { getValues } = methods; diff --git a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx index 390e4eccf2ba6..cba1a6d6a5e56 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx @@ -11,7 +11,7 @@ import { TextInput, } from '@rocket.chat/fuselage'; import { ContextualbarScrollableContent } from '@rocket.chat/ui-client'; -import { useCallback, useId, useMemo } from 'react'; +import { useCallback, useId, useMemo, Fragment } from 'react'; import { useFieldArray, useFormContext } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -35,29 +35,42 @@ const AttributesForm = ({ onSave, onCancel, description }: AttributesFormProps) watch, } = useFormContext(); + const { t } = useTranslation(); + + const attributeValues = watch('attributeValues'); + const lockedAttributes = watch('lockedAttributes'); + const { fields: lockedAttributesFields, remove: removeLockedAttribute } = useFieldArray({ name: 'lockedAttributes', }); + const validateRepeatedValues = useCallback( + (value: string) => { + // Only one instance of the same attribute value is allowed to be in the form at a time + const repeatedAttributes = [...lockedAttributes, ...attributeValues].filter((attribute) => attribute.value === value).length > 1; + return repeatedAttributes ? t('ABAC_No_repeated_values') : undefined; + }, + [lockedAttributes, attributeValues, t], + ); + const { fields, append, remove } = useFieldArray({ name: 'attributeValues', rules: { minLength: 1, }, }); - const { t } = useTranslation(); const formId = useId(); const nameField = useId(); const valuesField = useId(); - const attributeValues = watch('attributeValues'); const getAttributeValuesError = useCallback(() => { if (errors.attributeValues?.length && errors.attributeValues?.length > 0) { - return t('Required_field', { field: t('Values') }); + return errors.attributeValues[0]?.value?.message; } + return ''; - }, [errors.attributeValues, t]); + }, [errors.attributeValues]); const hasValuesErrors = useMemo(() => { const attributeValuesErrors = Array.isArray(errors?.attributeValues) && errors.attributeValues.some((error) => !!error?.value?.message); @@ -89,29 +102,42 @@ const AttributesForm = ({ onSave, onCancel, description }: AttributesFormProps) {t('Values')} {lockedAttributesFields.map((field, index) => ( - - - {index !== 0 && removeLockedAttribute(index)} />} - + + + validateRepeatedValues(value), + })} + /> + {index !== 0 && ( + removeLockedAttribute(index)} /> + )} + + {errors.lockedAttributes?.[index]?.value?.message || ''} + ))} {fields.map((field, index) => ( - - - {(index !== 0 || lockedAttributesFields.length > 0) && ( - remove(index)} /> - )} - + + + validateRepeatedValues(value), + })} + /> + {(index !== 0 || lockedAttributesFields.length > 0) && ( + remove(index)} /> + )} + + {errors.attributeValues?.[index]?.value?.message || ''} + ))} - {getAttributeValuesError()} + From c4bffe098c369d48a187f42059b013d48fbf1e5b Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Fri, 12 Dec 2025 14:47:02 -0300 Subject: [PATCH 5/7] fix: don't render empty fieldError --- .../views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx | 6 +++--- .../client/views/admin/ABAC/ABACRoomsTab/RoomForm.tsx | 2 +- .../admin/ABAC/ABACRoomsTab/RoomFormAttributeField.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx index cba1a6d6a5e56..35a83d97d30ca 100644 --- a/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx +++ b/apps/meteor/client/views/admin/ABAC/ABACAttributesTab/AttributesForm.tsx @@ -95,7 +95,7 @@ const AttributesForm = ({ onSave, onCancel, description }: AttributesFormProps) {...register('name', { required: t('Required_field', { field: t('Name') }) })} /> - {errors.name?.message || ''} + {errors.name && {errors.name.message}} @@ -117,7 +117,7 @@ const AttributesForm = ({ onSave, onCancel, description }: AttributesFormProps) removeLockedAttribute(index)} /> )} - {errors.lockedAttributes?.[index]?.value?.message || ''} + {errors.lockedAttributes?.[index]?.value && {errors.lockedAttributes?.[index]?.value?.message}} ))} {fields.map((field, index) => ( @@ -135,7 +135,7 @@ const AttributesForm = ({ onSave, onCancel, description }: AttributesFormProps) remove(index)} /> )} - {errors.attributeValues?.[index]?.value?.message || ''} + {errors.attributeValues?.[index]?.value && {errors.attributeValues[index].value.message}} ))} - @@ -322,9 +307,6 @@ exports[`AttributesForm renders WithLockedAttributes without crashing 1`] = ` -