11import type { OnyxCollection , OnyxEntry } from 'react-native-onyx' ;
22import { getIsOffline } from '@libs/NetworkState' ;
33import { computeReportName } from '@libs/ReportNameUtils' ;
4- import { generateIsEmptyReport , generateReportAttributes , hasVisibleReportFieldViolations , isArchivedReport , isValidReport } from '@libs/ReportUtils' ;
4+ import { generateIsEmptyReport , generateReportAttributes , hasVisibleReportFieldViolations , isArchivedReport , isPolicyAdmin , isPolicyExpenseChat , isValidReport } from '@libs/ReportUtils' ;
55import SidebarUtils from '@libs/SidebarUtils' ;
66import createOnyxDerivedValueConfig from '@userActions/OnyxDerived/createOnyxDerivedValueConfig' ;
77import { hasKeyTriggeredCompute } from '@userActions/OnyxDerived/utils' ;
@@ -283,6 +283,7 @@ export default createOnyxDerivedValueConfig({
283283 let brickRoadStatus ;
284284 let actionBadge ;
285285 let actionTargetReportActionID ;
286+ let needsParentChatErrorPropagation = false ;
286287 const reasonAndReportAction = SidebarUtils . getReasonAndReportActionThatHasRedBrickRoad (
287288 report ,
288289 chatReport ,
@@ -297,9 +298,19 @@ export default createOnyxDerivedValueConfig({
297298 ) ;
298299 // if report has errors or violations, show red dot
299300 if ( reasonAndReportAction ) {
300- brickRoadStatus = CONST . BRICK_ROAD_INDICATOR_STATUS . ERROR ;
301- actionBadge = CONST . REPORT . ACTION_BADGE . FIX ;
302- actionTargetReportActionID = reasonAndReportAction . reportAction ?. reportActionID ;
301+ needsParentChatErrorPropagation = true ;
302+
303+ // RBR/Fix mirrors GBR's access rule: only show on the child when the user can't already
304+ // see it on the parent workspace chat. The parent still gets ERROR/FIX through the
305+ // propagation loop below, so the actionable indicator surfaces on the workspace chat row
306+ // (which is where C+ wants it). Skips when the chat parent isn't accessible to the user.
307+ const chatPolicy = chatReport ?. policyID ? policies ?. [ `${ ONYXKEYS . COLLECTION . POLICY } ${ chatReport . policyID } ` ] : undefined ;
308+ const isChildOfAccessiblePolicyExpenseChat = ! ! chatReport && isPolicyExpenseChat ( chatReport ) && ( ! ! chatReport . isOwnPolicyExpenseChat || isPolicyAdmin ( chatPolicy ) ) ;
309+ if ( ! isChildOfAccessiblePolicyExpenseChat ) {
310+ brickRoadStatus = CONST . BRICK_ROAD_INDICATOR_STATUS . ERROR ;
311+ actionBadge = CONST . REPORT . ACTION_BADGE . FIX ;
312+ actionTargetReportActionID = reasonAndReportAction . reportAction ?. reportActionID ;
313+ }
303314 }
304315 // if report does not have error, check if it should show green dot
305316 if ( brickRoadStatus !== CONST . BRICK_ROAD_INDICATOR_STATUS . ERROR && requiresAttention ) {
@@ -330,6 +341,7 @@ export default createOnyxDerivedValueConfig({
330341 actionTargetReportActionID,
331342 reportErrors,
332343 oneTransactionThreadReportID,
344+ needsParentChatErrorPropagation,
333345 } ;
334346
335347 return acc ;
@@ -346,8 +358,15 @@ export default createOnyxDerivedValueConfig({
346358
347359 // If this is an IOU report and its calculated attributes have an error,
348360 // then we need to mark its parent chat report.
361+ // We read `needsParentChatErrorPropagation` rather than `brickRoadStatus` because the per-report
362+ // pass suppresses the child's own brickRoadStatus when the parent workspace chat is accessible —
363+ // we still need to propagate the error up so the parent shows the indicator.
349364 const attributes = reportAttributes [ report . reportID ] ;
350- if ( report . chatReportID && report . reportID !== report . chatReportID && attributes ?. brickRoadStatus === CONST . BRICK_ROAD_INDICATOR_STATUS . ERROR ) {
365+ if (
366+ report . chatReportID &&
367+ report . reportID !== report . chatReportID &&
368+ ( attributes ?. needsParentChatErrorPropagation || attributes ?. brickRoadStatus === CONST . BRICK_ROAD_INDICATOR_STATUS . ERROR )
369+ ) {
351370 chatReportIDsWithErrors . add ( report . chatReportID ) ;
352371 }
353372 }
0 commit comments