@@ -10,6 +10,7 @@ import useLocalize from '@hooks/useLocalize';
1010import useOnyx from '@hooks/useOnyx' ;
1111import usePermissions from '@hooks/usePermissions' ;
1212import usePolicyData from '@hooks/usePolicyData' ;
13+ import usePolicyFeatureWriteAccess from '@hooks/usePolicyFeatureWriteAccess' ;
1314import useThemeStyles from '@hooks/useThemeStyles' ;
1415
1516import Navigation from '@libs/Navigation/Navigation' ;
@@ -23,7 +24,7 @@ import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOpt
2324
2425import { enablePolicyCategories , setWorkspaceRequiresCategory } from '@userActions/Policy/Category' ;
2526import { clearPolicyErrorField } from '@userActions/Policy/Policy' ;
26- import { clearPolicyTagListErrorField , enablePolicyTags , setPolicyRequiresTag , setPolicyTagLevelsRequired , setPolicyTagsRequired } from '@userActions/Policy/Tag' ;
27+ import { clearPolicyTagListErrorField , enablePolicyTags , openPolicyTagsPage , setPolicyRequiresTag , setPolicyTagLevelsRequired , setPolicyTagsRequired } from '@userActions/Policy/Tag' ;
2728
2829import CONST from '@src/CONST' ;
2930import ONYXKEYS from '@src/ONYXKEYS' ;
@@ -48,6 +49,8 @@ function RulesRequireFieldsPage({
4849 const styles = useThemeStyles ( ) ;
4950 const { isBetaEnabled} = usePermissions ( ) ;
5051 const { showConfirmModal} = useConfirmModal ( ) ;
52+ // The self-heal below writes to the server, so it needs the same Tags write check the Tags table uses.
53+ const { canWrite : canWriteTags } = usePolicyFeatureWriteAccess ( policy , CONST . POLICY . POLICY_FEATURE . TAGS ) ;
5154 const isRulesRevampEnabled = isBetaEnabled ( CONST . BETAS . RULES_REVAMP ) ;
5255 const [ policyTags ] = useOnyx ( `${ ONYXKEYS . COLLECTION . POLICY_TAGS } ${ policyID } ` ) ;
5356
@@ -73,13 +76,42 @@ function RulesRequireFieldsPage({
7376 // Pending per-level edits until Save, keyed by orderWeight. Only toggled levels are here, so late-arriving tag lists aren't edits.
7477 const [ tagRequiredByLevel , setTagRequiredByLevel ] = useState < Record < number , boolean > > ( { } ) ;
7578 const syncedPolicyIDRef = useRef < string | undefined > ( undefined ) ;
79+ const hasRequestedTagsRef = useRef ( false ) ;
7680
7781 const getLevelRequired = useCallback ( ( tagList : ValueOf < PolicyTagLists > ) => tagRequiredByLevel [ tagList . orderWeight ] ?? ! ! tagList . required , [ tagRequiredByLevel ] ) ;
7882
7983 useEffect ( ( ) => {
8084 syncedPolicyIDRef . current = undefined ;
8185 } , [ policyID ] ) ;
8286
87+ useEffect ( ( ) => {
88+ // The General tab hydrates the tag lists, but it only mounts when it is the last selected Rules tab, so the
89+ // per-level rows would stay hidden when this page is opened directly. Fetch only when nothing loaded them.
90+ if ( policyTags || hasRequestedTagsRef . current ) {
91+ return ;
92+ }
93+
94+ hasRequestedTagsRef . current = true ;
95+ openPolicyTagsPage ( policyID ) ;
96+ } , [ policyID , policyTags ] ) ;
97+
98+ useEffect ( ( ) => {
99+ // Same self-heal as the Tags table: a required level with no enabled tags can never be satisfied, and it still
100+ // fires a violation, so clear it. Pending edits are skipped so this can't fight a toggle the admin just flipped.
101+ if ( ! canWriteTags || ! hasPerLevelTagRequired ) {
102+ return ;
103+ }
104+
105+ for ( const tagList of tagLists ) {
106+ const isAlreadyHandled = ! tagList . required || ! ! tagList . pendingFields ?. required || tagRequiredByLevel [ tagList . orderWeight ] !== undefined ;
107+ if ( isAlreadyHandled || hasEnabledOptions ( Object . values ( tagList . tags ?? { } ) ) ) {
108+ continue ;
109+ }
110+
111+ setPolicyTagsRequired ( policyData , false , tagList . orderWeight ) ;
112+ }
113+ } , [ canWriteTags , hasPerLevelTagRequired , policyData , tagLists , tagRequiredByLevel ] ) ;
114+
83115 useEffect ( ( ) => {
84116 if ( ! policy ?. id || policy . isLoading || syncedPolicyIDRef . current === policy . id ) {
85117 return ;
@@ -317,7 +349,7 @@ function RulesRequireFieldsPage({
317349 switchAccessibilityLabel = { label }
318350 shouldPlaceSubtitleBelowSwitch
319351 wrapperStyle = { styles . pv3 }
320- isActive = { getLevelRequired ( tagList ) && areLevelTagsEnabled }
352+ isActive = { getLevelRequired ( tagList ) }
321353 disabled = { isLevelToggleDisabled }
322354 showLockIcon = { shouldShowTagLock || isLastRequiredLevel ( tagList ) }
323355 disabledText = { tagDisabledText }
0 commit comments