@@ -10,7 +10,7 @@ import {
1010import { arrow_back , verified_user } from '@equinor/eds-icons' ;
1111import { useFramework } from '@equinor/fusion-framework-react' ;
1212import { useCurrentUser } from '@equinor/fusion-framework-react/hooks' ;
13- import { useEffect , useState } from 'react' ;
13+ import { type ReactElement , useEffect , useRef , useState } from 'react' ;
1414import styled from 'styled-components' ;
1515
1616import type { SheetContentProps } from '../types' ;
@@ -47,14 +47,24 @@ const Styled = {
4747 * @param props.navigate - Navigates back to the person side sheet landing page.
4848 * @returns A tabbed role overview with loading, error, and empty states.
4949 */
50- export const RolesSheetContent = ( { navigate } : SheetContentProps ) => {
50+ export const RolesSheetContent = ( { navigate } : SheetContentProps ) : ReactElement => {
5151 const framework = useFramework ( ) ;
5252 const user = useCurrentUser ( ) ;
5353 const [ tab , setTab ] = useState ( 0 ) ;
5454 const [ claimableRoles , setClaimableRoles ] = useState < ClaimableRoleAssignment [ ] > ( [ ] ) ;
5555 const [ permanentRoles , setPermanentRoles ] = useState < PermanentRoleAssignment [ ] > ( [ ] ) ;
5656 const [ isLoading , setIsLoading ] = useState ( true ) ;
5757 const [ error , setError ] = useState < string > ( ) ;
58+ const [ loadAttempt , setLoadAttempt ] = useState ( 0 ) ;
59+ const latestLoadAttempt = useRef ( loadAttempt ) ;
60+ latestLoadAttempt . current = loadAttempt ;
61+
62+ /** Starts a fresh role request after a retrieval failure. */
63+ const handleRetry = ( ) : void => {
64+ setError ( undefined ) ;
65+ setIsLoading ( true ) ;
66+ setLoadAttempt ( ( attempt ) => attempt + 1 ) ;
67+ } ;
5868
5969 /**
6070 * Replaces a changed assignment after activation or deactivation succeeds.
@@ -71,6 +81,7 @@ export const RolesSheetContent = ({ navigate }: SheetContentProps) => {
7181
7282 useEffect ( ( ) => {
7383 let isActive = true ;
84+ const currentLoadAttempt = loadAttempt ;
7485
7586 /** Loads both role collections together so the tabs represent one consistent snapshot. */
7687 const loadRoles = async ( ) : Promise < void > => {
@@ -90,19 +101,19 @@ export const RolesSheetContent = ({ navigate }: SheetContentProps) => {
90101 ] ) ;
91102
92103 // Ignore a completed request after the side sheet content has unmounted.
93- if ( isActive ) {
104+ if ( isActive && latestLoadAttempt . current === currentLoadAttempt ) {
94105 setClaimableRoles ( claimable ) ;
95106 setPermanentRoles ( permanent ) ;
96107 setError ( undefined ) ;
97108 }
98109 } catch ( cause ) {
99110 // Keep transport details out of the side sheet while preserving a useful retry direction.
100- if ( isActive ) {
111+ if ( isActive && latestLoadAttempt . current === currentLoadAttempt ) {
101112 setError ( cause instanceof Error ? cause . message : 'Failed to load roles.' ) ;
102113 }
103114 } finally {
104115 // Avoid updating state when navigation unmounts this sheet during a request.
105- if ( isActive ) {
116+ if ( isActive && latestLoadAttempt . current === currentLoadAttempt ) {
106117 setIsLoading ( false ) ;
107118 }
108119 }
@@ -113,7 +124,7 @@ export const RolesSheetContent = ({ navigate }: SheetContentProps) => {
113124 return ( ) => {
114125 isActive = false ;
115126 } ;
116- } , [ framework , user ?. localAccountId ] ) ;
127+ } , [ framework , user ?. localAccountId , loadAttempt ] ) ;
117128
118129 // Prepare role rows before markup so the tab panels only render presentation state.
119130 const claimableItems = claimableRoles . map ( ( assignment ) => (
@@ -154,9 +165,14 @@ export const RolesSheetContent = ({ navigate }: SheetContentProps) => {
154165 { isLoading ? (
155166 < CircularProgress aria-label = "Loading roles" />
156167 ) : error ? (
157- < Banner >
158- < Banner . Message > { error } </ Banner . Message >
159- </ Banner >
168+ < >
169+ < Banner >
170+ < Banner . Message > { error } </ Banner . Message >
171+ </ Banner >
172+ < Button variant = "outlined" onClick = { handleRetry } >
173+ Retry
174+ </ Button >
175+ </ >
160176 ) : (
161177 < Tabs activeTab = { tab } onChange = { ( index ) => setTab ( Number ( index ) ) } >
162178 < Tabs . List >
0 commit comments