Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: project-predictions #600

Merged
merged 5 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pr-600-1771708999.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

---
"fusion-project-portal": minor
---
Project prediction on landing page
1 change: 1 addition & 0 deletions client/packages/core/src/user/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './useUserInfo';
export * from './userPhoto';
export * from './useUserContexts';
59 changes: 59 additions & 0 deletions client/packages/core/src/user/hooks/useUserContexts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useFramework } from '@equinor/fusion-framework-react';
import { useMemo } from 'react';
import { useQuery } from 'react-query';
import { RelationReturnType, Relations } from '../../context';
import { useCurrentUser } from './useUserInfo';

import { from, lastValueFrom, map, mergeMap, reduce, switchMap } from 'rxjs';

type ID = { id: string };

export const useUserOrgDetails = (viewAll?: boolean) => {
const { data: user } = useCurrentUser();

const serviceProvider = useFramework().modules.serviceDiscovery;

const projects = useMemo(() => {
return user?.positions
?.filter((item) => viewAll || (item.appliesTo && new Date(item.appliesTo) > new Date()))
.map((position) => position.project.id);
}, [user, viewAll]);

return useQuery<RelationReturnType<'ProjectMaster'>[], Error>({
queryKey: ['pro-to-proM ', JSON.stringify(projects?.join), viewAll],
queryFn: async () => {
const contextClient = await serviceProvider.createClient('context');

const gg = contextClient
.json$<ID[]>(
`contexts/?$filter=type eq orgchart and (${projects
?.map((id) => `externalID eq ${id}`)
.join(' or ')})`
)
.pipe(
switchMap((items) =>
from(items).pipe(
mergeMap((item) =>
contextClient
.json$<Relations[]>(`contexts/${item.id}/relations`)
.pipe(
map((x) =>
x.filter((p): p is RelationReturnType<'ProjectMaster'> =>
p.relationSource.includes('ProjectMaster')
)
)
)
),
reduce(
(acc, items) => [...acc, ...items].sort((i, o) => (i.title < o.title ? -1 : 1)),
[] as RelationReturnType<'ProjectMaster'>[]
)
)
)
);
return lastValueFrom(gg);
},
enabled: Boolean(projects && projects?.length > 0),
_defaulted: undefined,
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,13 @@ export function createPortalFramework(portalConfig: PortalConfig) {
{
key: 'new-menu',
title: 'New Portal Menu',
description: 'When enabled you will be able to tryout the new portal menu',
description: 'When enabled you will be able to try out the new portal menu',
enabled: true,
},
{
key: 'project-prediction',
title: 'Project Prediction',
description: 'When enabled you will get project prediction on the project portal landing page',
enabled: true,
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import styled from 'styled-components';
import { User } from './user/UserCard';

import InfoBox from '../sheared/components/InfoBox/InfoBox';
import { useUserOrgDetails } from '@portal/core';
import { Checkbox, LinearProgress, Typography } from '@equinor/eds-core-react';

import { useState } from 'react';
import { useFeature } from '@equinor/fusion-framework-react-app/feature-flag';

const styles = {
contentSection: css`
Expand All @@ -24,9 +29,6 @@ const styles = {
flex-direction: column;
gap: 1rem;
`,
viewDescription: css`
width: 50vw;
`,
};

export const Styles = {
Expand All @@ -35,6 +37,12 @@ export const Styles = {
flex-direction: column;
`,

Section: styled.span`
width: 40vw;
display: flex;
flex-direction: column;
gap: 0.5rem;
`,
Content: styled.section`
padding: 0rem 2rem;
height: 100vh;
Expand Down Expand Up @@ -77,9 +85,39 @@ export const Styles = {
flex: 1;
flex-direction: column;
`,
Heading: styled.div`
padding-top: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
`,
LinkWrapper: styled.span`
display: flex;
align-items: center;
gap: 0.5rem;
`,
Nav: styled.nav`
padding: 1rem 0;
gap: 0.5rem;
display: flex;
flex-wrap: wrap;
width: 25%;
`,
Loading: styled.div`
padding: 1rem 0;
width: 25%;
`,
Padding: styled.span`
padding: 2rem;
`,
};

export const ProjectPortalPage = (): JSX.Element => {
const [value, setValue] = useState(false);
const { data, isLoading } = useUserOrgDetails(value);
const { feature } = useFeature('project-prediction');

return (
<Styles.Wrapper>
<ProjectHeader>
Expand All @@ -89,15 +127,65 @@ export const ProjectPortalPage = (): JSX.Element => {
</Styles.Details>
<Styles.Content>
<div className={styles.contentWrapper}>
<p className={styles.viewDescription}>
Please choose a project or facility from the search field to continue. This will direct you
to the context's homepage, where you can access the applications associated with the
selected context through the menu.
</p>
<Styles.Section>
<Typography>
Please choose a project or facility from the search field to continue. This will direct
you to the context's homepage, where you can access the applications associated with the
selected context through the menu.
</Typography>
</Styles.Section>
<ContextProvider>
<PortalContextSelector />
</ContextProvider>
</div>
{feature?.enabled && (
<Styles.Padding>
<Styles.Section>
<Styles.Heading>
<Typography variant="h5">Allocated Projects</Typography>
<Checkbox
label="Use all past allocations"
checked={value}
onChange={() => {
setValue((s) => !s);
}}
/>
</Styles.Heading>
<Typography>
By analyzing your allocations, we can provide predictions regarding which projects
you may want to prioritize. Here is a list of {value ? 'all your' : 'your current'}{' '}
projects:
</Typography>
</Styles.Section>
{isLoading ? (
<Styles.Loading>
<LinearProgress />
<Typography>Analyzing your allocations...</Typography>
</Styles.Loading>
) : (
<Styles.Nav>
{data && data.length > 0 ? (
data.map((item, index) => (
<Styles.LinkWrapper key={item.id}>
<Typography link title={item.title} href={`/project/${item.id}`}>
{item.title}
</Typography>
{data.length > index + 1 && <span>|</span>}
</Styles.LinkWrapper>
))
) : (
<>
{data && (
<Typography variant="overline">
Sorry, we could not find any projects from your allocations.
</Typography>
)}
</>
)}
</Styles.Nav>
)}
</Styles.Padding>
)}
</Styles.Content>
</ProjectHeader>
</Styles.Wrapper>
Expand Down
Loading