mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: handle loading states for project details for a single project (#8492)
This PR updates the use of references on the project details page to handle the loading state for a single project. Now, if a project is loading, it'll show skeleton loaders for the relevant boxes:  I've also updated the state type we use for this to be more accurate. Shamelessly stolen from Elm. ```ts type RemoteData<T> = | { state: 'error', error: Error } | { state: 'loading' } | { state: 'success', data: T } ``` After refactoring: 
This commit is contained in:
		
							parent
							
								
									9fecc02462
								
							
						
					
					
						commit
						a8206f5118
					
				@ -1,9 +1,11 @@
 | 
			
		||||
import type { RemoteData } from './RemoteData';
 | 
			
		||||
import {
 | 
			
		||||
    Box,
 | 
			
		||||
    IconButton,
 | 
			
		||||
    ListItem,
 | 
			
		||||
    ListItemButton,
 | 
			
		||||
    Typography,
 | 
			
		||||
    styled,
 | 
			
		||||
} from '@mui/material';
 | 
			
		||||
import { ProjectIcon } from '../common/ProjectIcon/ProjectIcon';
 | 
			
		||||
import LinkIcon from '@mui/icons-material/ArrowForward';
 | 
			
		||||
@ -11,9 +13,10 @@ import { ProjectSetupComplete } from './ProjectSetupComplete';
 | 
			
		||||
import { ConnectSDK, CreateFlag, ExistingFlag } from './ConnectSDK';
 | 
			
		||||
import { LatestProjectEvents } from './LatestProjectEvents';
 | 
			
		||||
import { RoleAndOwnerInfo } from './RoleAndOwnerInfo';
 | 
			
		||||
import { type ReactNode, forwardRef, useEffect, useRef, type FC } from 'react';
 | 
			
		||||
import { type ReactNode, useEffect, useRef, type FC } from 'react';
 | 
			
		||||
import type {
 | 
			
		||||
    PersonalDashboardProjectDetailsSchema,
 | 
			
		||||
    PersonalDashboardProjectDetailsSchemaRolesItem,
 | 
			
		||||
    PersonalDashboardSchemaAdminsItem,
 | 
			
		||||
    PersonalDashboardSchemaProjectOwnersItem,
 | 
			
		||||
    PersonalDashboardSchemaProjectsItem,
 | 
			
		||||
@ -33,6 +36,7 @@ import { ContactAdmins, DataError } from './ProjectDetailsError';
 | 
			
		||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
			
		||||
import { Link } from 'react-router-dom';
 | 
			
		||||
import { ActionBox } from './ActionBox';
 | 
			
		||||
import useLoading from 'hooks/useLoading';
 | 
			
		||||
import { NoProjectsContactAdmin } from './NoProjectsContactAdmin';
 | 
			
		||||
import { AskOwnerToAddYouToTheirProject } from './AskOwnerToAddYouToTheirProject';
 | 
			
		||||
 | 
			
		||||
@ -69,6 +73,10 @@ const ActiveProjectDetails: FC<{
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SkeletonDiv = styled('div')({
 | 
			
		||||
    height: '80%',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ProjectListItem: FC<{
 | 
			
		||||
    project: PersonalDashboardSchemaProjectsItem;
 | 
			
		||||
    selected: boolean;
 | 
			
		||||
@ -122,186 +130,145 @@ const ProjectListItem: FC<{
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type MyProjectsState = 'no projects' | 'projects' | 'projects with error';
 | 
			
		||||
export const MyProjects: React.FC<{
 | 
			
		||||
    projects: PersonalDashboardSchemaProjectsItem[];
 | 
			
		||||
    personalDashboardProjectDetails: RemoteData<PersonalDashboardProjectDetailsSchema>;
 | 
			
		||||
    activeProject: string;
 | 
			
		||||
    setActiveProject: (project: string) => void;
 | 
			
		||||
    admins: PersonalDashboardSchemaAdminsItem[];
 | 
			
		||||
    owners: PersonalDashboardSchemaProjectOwnersItem[];
 | 
			
		||||
}> = ({
 | 
			
		||||
    projects,
 | 
			
		||||
    personalDashboardProjectDetails,
 | 
			
		||||
    setActiveProject,
 | 
			
		||||
    activeProject,
 | 
			
		||||
    admins,
 | 
			
		||||
    owners,
 | 
			
		||||
}) => {
 | 
			
		||||
    const ref = useLoading(personalDashboardProjectDetails.state === 'loading');
 | 
			
		||||
 | 
			
		||||
export const MyProjects = forwardRef<
 | 
			
		||||
    HTMLDivElement,
 | 
			
		||||
    {
 | 
			
		||||
        projects: PersonalDashboardSchemaProjectsItem[];
 | 
			
		||||
        personalDashboardProjectDetails?: PersonalDashboardProjectDetailsSchema;
 | 
			
		||||
        activeProject: string;
 | 
			
		||||
        setActiveProject: (project: string) => void;
 | 
			
		||||
        admins: PersonalDashboardSchemaAdminsItem[];
 | 
			
		||||
        owners: PersonalDashboardSchemaProjectOwnersItem[];
 | 
			
		||||
    }
 | 
			
		||||
>(
 | 
			
		||||
    (
 | 
			
		||||
        {
 | 
			
		||||
            projects,
 | 
			
		||||
            personalDashboardProjectDetails,
 | 
			
		||||
            setActiveProject,
 | 
			
		||||
            activeProject,
 | 
			
		||||
            admins,
 | 
			
		||||
            owners,
 | 
			
		||||
        },
 | 
			
		||||
        ref,
 | 
			
		||||
    ) => {
 | 
			
		||||
        const state: MyProjectsState = projects.length
 | 
			
		||||
            ? personalDashboardProjectDetails
 | 
			
		||||
                ? 'projects'
 | 
			
		||||
                : 'projects with error'
 | 
			
		||||
            : 'no projects';
 | 
			
		||||
    const getGridContents = (): {
 | 
			
		||||
        list: ReactNode;
 | 
			
		||||
        box1: ReactNode;
 | 
			
		||||
        box2: ReactNode;
 | 
			
		||||
    } => {
 | 
			
		||||
        if (projects.length === 0) {
 | 
			
		||||
            return {
 | 
			
		||||
                list: (
 | 
			
		||||
                    <ActionBox>
 | 
			
		||||
                        <Typography>
 | 
			
		||||
                            You don't currently have access to any projects in
 | 
			
		||||
                            the system.
 | 
			
		||||
                        </Typography>
 | 
			
		||||
                        <Typography>
 | 
			
		||||
                            To get started, you can{' '}
 | 
			
		||||
                            <Link to='/projects?create=true'>
 | 
			
		||||
                                create your own project
 | 
			
		||||
                            </Link>
 | 
			
		||||
                            . Alternatively, you can review the available
 | 
			
		||||
                            projects in the system and ask the owner for access.
 | 
			
		||||
                        </Typography>
 | 
			
		||||
                    </ActionBox>
 | 
			
		||||
                ),
 | 
			
		||||
                box1: <NoProjectsContactAdmin admins={admins} />,
 | 
			
		||||
                box2: <AskOwnerToAddYouToTheirProject owners={owners} />,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const activeProjectStage =
 | 
			
		||||
            personalDashboardProjectDetails?.onboardingStatus.status ??
 | 
			
		||||
            'loading';
 | 
			
		||||
        const setupIncomplete =
 | 
			
		||||
            activeProjectStage === 'onboarding-started' ||
 | 
			
		||||
            activeProjectStage === 'first-flag-created';
 | 
			
		||||
 | 
			
		||||
        const getGridContents = (): {
 | 
			
		||||
            list: ReactNode;
 | 
			
		||||
            box1: ReactNode;
 | 
			
		||||
            box2: ReactNode;
 | 
			
		||||
        } => {
 | 
			
		||||
            switch (state) {
 | 
			
		||||
                case 'no projects':
 | 
			
		||||
                    return {
 | 
			
		||||
                        list: (
 | 
			
		||||
                            <ActionBox>
 | 
			
		||||
                                <Typography>
 | 
			
		||||
                                    You don't currently have access to any
 | 
			
		||||
                                    projects in the system.
 | 
			
		||||
                                </Typography>
 | 
			
		||||
                                <Typography>
 | 
			
		||||
                                    To get started, you can{' '}
 | 
			
		||||
                                    <Link to='/projects?create=true'>
 | 
			
		||||
                                        create your own project
 | 
			
		||||
                                    </Link>
 | 
			
		||||
                                    . Alternatively, you can review the
 | 
			
		||||
                                    available projects in the system and ask the
 | 
			
		||||
                                    owner for access.
 | 
			
		||||
                                </Typography>
 | 
			
		||||
                            </ActionBox>
 | 
			
		||||
                        ),
 | 
			
		||||
                        box1: <NoProjectsContactAdmin admins={admins} />,
 | 
			
		||||
                        box2: (
 | 
			
		||||
                            <AskOwnerToAddYouToTheirProject owners={owners} />
 | 
			
		||||
                        ),
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                case 'projects with error':
 | 
			
		||||
                    return {
 | 
			
		||||
                        list: (
 | 
			
		||||
                            <StyledList>
 | 
			
		||||
                                {projects.map((project) => (
 | 
			
		||||
                                    <ProjectListItem
 | 
			
		||||
                                        key={project.id}
 | 
			
		||||
                                        project={project}
 | 
			
		||||
                                        selected={project.id === activeProject}
 | 
			
		||||
                                        onClick={() =>
 | 
			
		||||
                                            setActiveProject(project.id)
 | 
			
		||||
                                        }
 | 
			
		||||
                                    />
 | 
			
		||||
                                ))}
 | 
			
		||||
                            </StyledList>
 | 
			
		||||
                        ),
 | 
			
		||||
                        box1: <DataError project={activeProject} />,
 | 
			
		||||
                        box2: <ContactAdmins admins={admins} />,
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                case 'projects': {
 | 
			
		||||
                    const box1 = (() => {
 | 
			
		||||
                        if (
 | 
			
		||||
                            activeProjectStage === 'onboarded' &&
 | 
			
		||||
                            personalDashboardProjectDetails
 | 
			
		||||
                        ) {
 | 
			
		||||
                            return (
 | 
			
		||||
                                <ProjectSetupComplete
 | 
			
		||||
                                    project={activeProject}
 | 
			
		||||
                                    insights={
 | 
			
		||||
                                        personalDashboardProjectDetails.insights
 | 
			
		||||
                                    }
 | 
			
		||||
                                />
 | 
			
		||||
                            );
 | 
			
		||||
                        } else if (
 | 
			
		||||
                            activeProjectStage === 'onboarding-started' ||
 | 
			
		||||
                            activeProjectStage === 'loading'
 | 
			
		||||
                        ) {
 | 
			
		||||
                            return <CreateFlag project={activeProject} />;
 | 
			
		||||
                        } else if (
 | 
			
		||||
                            activeProjectStage === 'first-flag-created'
 | 
			
		||||
                        ) {
 | 
			
		||||
                            return <ExistingFlag project={activeProject} />;
 | 
			
		||||
                        }
 | 
			
		||||
                    })();
 | 
			
		||||
 | 
			
		||||
                    const box2 = (() => {
 | 
			
		||||
                        if (
 | 
			
		||||
                            activeProjectStage === 'onboarded' &&
 | 
			
		||||
                            personalDashboardProjectDetails
 | 
			
		||||
                        ) {
 | 
			
		||||
                            return (
 | 
			
		||||
                                <LatestProjectEvents
 | 
			
		||||
                                    latestEvents={
 | 
			
		||||
                                        personalDashboardProjectDetails.latestEvents
 | 
			
		||||
                                    }
 | 
			
		||||
                                />
 | 
			
		||||
                            );
 | 
			
		||||
                        } else if (
 | 
			
		||||
                            setupIncomplete ||
 | 
			
		||||
                            activeProjectStage === 'loading'
 | 
			
		||||
                        ) {
 | 
			
		||||
                            return <ConnectSDK project={activeProject} />;
 | 
			
		||||
                        }
 | 
			
		||||
                    })();
 | 
			
		||||
 | 
			
		||||
                    return {
 | 
			
		||||
                        list: (
 | 
			
		||||
                            <StyledList>
 | 
			
		||||
                                {projects.map((project) => (
 | 
			
		||||
                                    <ProjectListItem
 | 
			
		||||
                                        key={project.id}
 | 
			
		||||
                                        project={project}
 | 
			
		||||
                                        selected={project.id === activeProject}
 | 
			
		||||
                                        onClick={() =>
 | 
			
		||||
                                            setActiveProject(project.id)
 | 
			
		||||
                                        }
 | 
			
		||||
                                    />
 | 
			
		||||
                                ))}
 | 
			
		||||
                            </StyledList>
 | 
			
		||||
                        ),
 | 
			
		||||
                        box1,
 | 
			
		||||
                        box2,
 | 
			
		||||
                    };
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const { list, box1, box2 } = getGridContents();
 | 
			
		||||
        return (
 | 
			
		||||
            <ContentGridContainer ref={ref}>
 | 
			
		||||
                <ProjectGrid>
 | 
			
		||||
                    <SpacedGridItem gridArea='projects'>{list}</SpacedGridItem>
 | 
			
		||||
                    <SpacedGridItem gridArea='box1'>{box1}</SpacedGridItem>
 | 
			
		||||
                    <SpacedGridItem gridArea='box2'>{box2}</SpacedGridItem>
 | 
			
		||||
                    <EmptyGridItem />
 | 
			
		||||
                    <GridItem gridArea='owners'>
 | 
			
		||||
                        <RoleAndOwnerInfo
 | 
			
		||||
                            roles={
 | 
			
		||||
                                personalDashboardProjectDetails?.roles.map(
 | 
			
		||||
                                    (role) => role.name,
 | 
			
		||||
                                ) ?? []
 | 
			
		||||
                            }
 | 
			
		||||
                            owners={
 | 
			
		||||
                                personalDashboardProjectDetails?.owners ?? [
 | 
			
		||||
                                    { ownerType: 'user', name: '?' },
 | 
			
		||||
                                ]
 | 
			
		||||
                            }
 | 
			
		||||
                        />
 | 
			
		||||
                    </GridItem>
 | 
			
		||||
                </ProjectGrid>
 | 
			
		||||
            </ContentGridContainer>
 | 
			
		||||
        const list = (
 | 
			
		||||
            <StyledList>
 | 
			
		||||
                {projects.map((project) => (
 | 
			
		||||
                    <ProjectListItem
 | 
			
		||||
                        key={project.id}
 | 
			
		||||
                        project={project}
 | 
			
		||||
                        selected={project.id === activeProject}
 | 
			
		||||
                        onClick={() => setActiveProject(project.id)}
 | 
			
		||||
                    />
 | 
			
		||||
                ))}
 | 
			
		||||
            </StyledList>
 | 
			
		||||
        );
 | 
			
		||||
    },
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
        const [box1, box2] = (() => {
 | 
			
		||||
            switch (personalDashboardProjectDetails.state) {
 | 
			
		||||
                case 'success': {
 | 
			
		||||
                    const activeProjectStage =
 | 
			
		||||
                        personalDashboardProjectDetails.data.onboardingStatus
 | 
			
		||||
                            .status ?? 'loading';
 | 
			
		||||
                    const setupIncomplete =
 | 
			
		||||
                        activeProjectStage === 'onboarding-started' ||
 | 
			
		||||
                        activeProjectStage === 'first-flag-created';
 | 
			
		||||
 | 
			
		||||
                    if (activeProjectStage === 'onboarded') {
 | 
			
		||||
                        return [
 | 
			
		||||
                            <ProjectSetupComplete
 | 
			
		||||
                                project={activeProject}
 | 
			
		||||
                                insights={
 | 
			
		||||
                                    personalDashboardProjectDetails.data
 | 
			
		||||
                                        .insights
 | 
			
		||||
                                }
 | 
			
		||||
                            />,
 | 
			
		||||
                            <LatestProjectEvents
 | 
			
		||||
                                latestEvents={
 | 
			
		||||
                                    personalDashboardProjectDetails.data
 | 
			
		||||
                                        .latestEvents
 | 
			
		||||
                                }
 | 
			
		||||
                            />,
 | 
			
		||||
                        ];
 | 
			
		||||
                    } else if (setupIncomplete) {
 | 
			
		||||
                        return [
 | 
			
		||||
                            <CreateFlag project={activeProject} />,
 | 
			
		||||
                            <ConnectSDK project={activeProject} />,
 | 
			
		||||
                        ];
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return [
 | 
			
		||||
                            <ExistingFlag project={activeProject} />,
 | 
			
		||||
                            <ConnectSDK project={activeProject} />,
 | 
			
		||||
                        ];
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                case 'error':
 | 
			
		||||
                    return [
 | 
			
		||||
                        <DataError project={activeProject} />,
 | 
			
		||||
                        <ContactAdmins admins={admins} />,
 | 
			
		||||
                    ];
 | 
			
		||||
                default: // loading
 | 
			
		||||
                    return [
 | 
			
		||||
                        <SkeletonDiv data-loading />,
 | 
			
		||||
                        <SkeletonDiv data-loading />,
 | 
			
		||||
                    ];
 | 
			
		||||
            }
 | 
			
		||||
        })();
 | 
			
		||||
 | 
			
		||||
        return { list, box1, box2 };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const { list, box1, box2 } = getGridContents();
 | 
			
		||||
    return (
 | 
			
		||||
        <ContentGridContainer ref={ref}>
 | 
			
		||||
            <ProjectGrid>
 | 
			
		||||
                <SpacedGridItem gridArea='projects'>{list}</SpacedGridItem>
 | 
			
		||||
                <SpacedGridItem gridArea='box1'>{box1}</SpacedGridItem>
 | 
			
		||||
                <SpacedGridItem gridArea='box2'>{box2}</SpacedGridItem>
 | 
			
		||||
                <EmptyGridItem />
 | 
			
		||||
                <GridItem gridArea='owners'>
 | 
			
		||||
                    <RoleAndOwnerInfo
 | 
			
		||||
                        roles={
 | 
			
		||||
                            personalDashboardProjectDetails.state === 'success'
 | 
			
		||||
                                ? personalDashboardProjectDetails.data.roles.map(
 | 
			
		||||
                                      (
 | 
			
		||||
                                          role: PersonalDashboardProjectDetailsSchemaRolesItem,
 | 
			
		||||
                                      ) => role.name,
 | 
			
		||||
                                  )
 | 
			
		||||
                                : []
 | 
			
		||||
                        }
 | 
			
		||||
                        owners={
 | 
			
		||||
                            personalDashboardProjectDetails.state === 'success'
 | 
			
		||||
                                ? personalDashboardProjectDetails.data.owners
 | 
			
		||||
                                : [{ ownerType: 'user', name: '?' }]
 | 
			
		||||
                        }
 | 
			
		||||
                    />
 | 
			
		||||
                </GridItem>
 | 
			
		||||
            </ProjectGrid>
 | 
			
		||||
        </ContentGridContainer>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,6 @@ import { WelcomeDialog } from './WelcomeDialog';
 | 
			
		||||
import { useLocalStorageState } from 'hooks/useLocalStorageState';
 | 
			
		||||
import { usePersonalDashboard } from 'hooks/api/getters/usePersonalDashboard/usePersonalDashboard';
 | 
			
		||||
import { usePersonalDashboardProjectDetails } from 'hooks/api/getters/usePersonalDashboard/usePersonalDashboardProjectDetails';
 | 
			
		||||
import useLoading from '../../hooks/useLoading';
 | 
			
		||||
import { MyProjects } from './MyProjects';
 | 
			
		||||
import ExpandMore from '@mui/icons-material/ExpandMore';
 | 
			
		||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
 | 
			
		||||
@ -20,6 +19,7 @@ import { useAuthSplash } from 'hooks/api/getters/useAuth/useAuthSplash';
 | 
			
		||||
import { useDashboardState } from './useDashboardState';
 | 
			
		||||
import { MyFlags } from './MyFlags';
 | 
			
		||||
import { usePageTitle } from 'hooks/usePageTitle';
 | 
			
		||||
import { fromPersonalDashboardProjectDetailsOutput } from './RemoteData';
 | 
			
		||||
 | 
			
		||||
const WelcomeSection = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
@ -130,15 +130,10 @@ export const PersonalDashboard = () => {
 | 
			
		||||
        splash?.personalDashboardKeyConcepts ? 'closed' : 'open',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const { personalDashboardProjectDetails, error: detailsError } =
 | 
			
		||||
        usePersonalDashboardProjectDetails(activeProject);
 | 
			
		||||
 | 
			
		||||
    const activeProjectStage =
 | 
			
		||||
        personalDashboardProjectDetails?.onboardingStatus.status ?? 'loading';
 | 
			
		||||
 | 
			
		||||
    const projectStageRef = useLoading(
 | 
			
		||||
        !detailsError && activeProjectStage === 'loading',
 | 
			
		||||
    );
 | 
			
		||||
    const personalDashboardProjectDetails =
 | 
			
		||||
        fromPersonalDashboardProjectDetailsOutput(
 | 
			
		||||
            usePersonalDashboardProjectDetails(activeProject),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <MainContent>
 | 
			
		||||
@ -192,7 +187,6 @@ export const PersonalDashboard = () => {
 | 
			
		||||
                    <MyProjects
 | 
			
		||||
                        owners={personalDashboard?.projectOwners ?? []}
 | 
			
		||||
                        admins={personalDashboard?.admins ?? []}
 | 
			
		||||
                        ref={projectStageRef}
 | 
			
		||||
                        projects={projects}
 | 
			
		||||
                        activeProject={activeProject || ''}
 | 
			
		||||
                        setActiveProject={setActiveProject}
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,7 @@ import { ActionBox } from './ActionBox';
 | 
			
		||||
 | 
			
		||||
export const DataError: FC<{ project: string }> = ({ project }) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <ActionBox
 | 
			
		||||
            data-loading
 | 
			
		||||
            title={`Couldn't fetch data for project "${project}".`}
 | 
			
		||||
        >
 | 
			
		||||
        <ActionBox title={`Couldn't fetch data for project "${project}".`}>
 | 
			
		||||
            <p>
 | 
			
		||||
                The API request to get data for this project returned with an
 | 
			
		||||
                error.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								frontend/src/component/personalDashboard/RemoteData.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								frontend/src/component/personalDashboard/RemoteData.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
import type { IPersonalDashboardProjectDetailsOutput } from 'hooks/api/getters/usePersonalDashboard/usePersonalDashboardProjectDetails';
 | 
			
		||||
import type { PersonalDashboardProjectDetailsSchema } from 'openapi';
 | 
			
		||||
 | 
			
		||||
export type RemoteData<T> =
 | 
			
		||||
    | { state: 'error'; error: Error }
 | 
			
		||||
    | { state: 'loading' }
 | 
			
		||||
    | { state: 'success'; data: T };
 | 
			
		||||
 | 
			
		||||
export const fromPersonalDashboardProjectDetailsOutput = ({
 | 
			
		||||
    personalDashboardProjectDetails,
 | 
			
		||||
    error,
 | 
			
		||||
}: IPersonalDashboardProjectDetailsOutput): RemoteData<PersonalDashboardProjectDetailsSchema> => {
 | 
			
		||||
    const converted = error
 | 
			
		||||
        ? {
 | 
			
		||||
              state: 'error',
 | 
			
		||||
              error,
 | 
			
		||||
          }
 | 
			
		||||
        : personalDashboardProjectDetails
 | 
			
		||||
          ? {
 | 
			
		||||
                state: 'success',
 | 
			
		||||
                data: personalDashboardProjectDetails,
 | 
			
		||||
            }
 | 
			
		||||
          : {
 | 
			
		||||
                state: 'loading' as const,
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
    return converted as RemoteData<PersonalDashboardProjectDetailsSchema>;
 | 
			
		||||
};
 | 
			
		||||
@ -51,7 +51,7 @@ export const RoleAndOwnerInfo = ({ roles, owners }: Props) => {
 | 
			
		||||
    const firstRoles = roles.slice(0, 3);
 | 
			
		||||
    const extraRoles = roles.slice(3);
 | 
			
		||||
    return (
 | 
			
		||||
        <Wrapper>
 | 
			
		||||
        <Wrapper data-loading>
 | 
			
		||||
            <InfoSection>
 | 
			
		||||
                {roles.length > 0 ? (
 | 
			
		||||
                    <>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user