-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: Update tsconfig.json and remove unused imports
- Update tsconfig.json to target ESNext and update lib options - Remove unused imports in UserCard.tsx and ProjectPortalPage.tsx - Export useRelationsByType and types/relations from context/index.ts - Update package.json version to 0.0.3 - Add useRelationsByType hook to context/hooks/use-relations-by-type.ts - Add useContextRelationsQuery and getContextRelations to context/queries/get-context-relations.ts - Add ProfileCardHeader component to components/ProfileCardHeader.tsx - Add Skeleton component to components/skeleton/Skeleton.tsx
- Loading branch information
Showing
11 changed files
with
455 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
client/apps/project-portal-landingpage/src/components/ProfileCardHeader.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import styled from 'styled-components'; | ||
import { Typography } from '@equinor/eds-core-react'; | ||
|
||
import { PersonAvatar } from '@equinor/fusion-react-person'; | ||
import { PersonDetails } from '../types/person-details'; | ||
import { Skeleton } from './skeleton/Skeleton'; | ||
import { getDepartment, getJobTitle } from '../hooks/user'; | ||
|
||
const Style = { | ||
InfoWrapper: styled.div<{ paddingNone?: boolean }>` | ||
display: flex; | ||
align-items: center; | ||
gap: 1rem; | ||
padding: ${({ paddingNone }) => (paddingNone ? '0px' : '1rem')}; | ||
`, | ||
}; | ||
|
||
export const ProfileCardHeader = ({ | ||
user, | ||
trigger = 'none', | ||
paddingNone, | ||
}: { | ||
user?: PersonDetails; | ||
trigger?: 'click' | 'hover' | 'none'; | ||
paddingNone?: boolean; | ||
}) => { | ||
if (!user) { | ||
return ( | ||
<Style.InfoWrapper paddingNone={paddingNone}> | ||
<Skeleton variant="circle" size="medium" /> | ||
|
||
<div> | ||
<Skeleton width="200px" /> | ||
<div style={{ paddingTop: '0.5rem', gap: '0.2rem', display: 'flex', flexDirection: 'column' }}> | ||
<Skeleton width={60} /> | ||
<Skeleton width={60} /> | ||
</div> | ||
</div> | ||
</Style.InfoWrapper> | ||
); | ||
} | ||
|
||
return ( | ||
<Style.InfoWrapper paddingNone={paddingNone}> | ||
<PersonAvatar azureId={user.azureUniqueId} trigger={trigger} /> | ||
<div> | ||
<Typography variant="h6">{user?.name}</Typography> | ||
<div> | ||
<Typography>{getDepartment(user)}</Typography> | ||
<Typography>{getJobTitle(user)}</Typography> | ||
</div> | ||
</div> | ||
</Style.InfoWrapper> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
client/apps/project-portal-landingpage/src/components/skeleton/Skeleton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import FusionSkeleton, { SkeletonSize, SkeletonVariant } from '@equinor/fusion-react-skeleton'; | ||
import { CSSProperties, FC } from 'react'; | ||
|
||
type SkeletonProps = { | ||
width?: number | string; | ||
height?: number | string; | ||
size?: keyof typeof skeletonSize; | ||
variant?: keyof typeof skeletonVariant; | ||
fluid?: boolean; | ||
}; | ||
|
||
const skeletonVariant = { | ||
circle: SkeletonVariant.Circle, | ||
rectangle: SkeletonVariant.Rectangle, | ||
square: SkeletonVariant.Square, | ||
text: SkeletonVariant.Text, | ||
}; | ||
|
||
const skeletonSize = { | ||
xSmall: SkeletonSize.XSmall, | ||
small: SkeletonSize.small, | ||
large: SkeletonSize.Large, | ||
medium: SkeletonSize.Medium, | ||
}; | ||
|
||
/** | ||
* Skeleton Component | ||
* | ||
* The `Skeleton` component is a simplified ree-export of `@equinor/fusion-react-skeleton` a React component used to render skeleton loading elements. | ||
* | ||
* @param width - number (optional) - Specifies the width of the skeleton element in present% | ||
* @param type - string (optional) - Specifies the type of skeleton to render. Should be one of "xSmall" | "small" | "large" | "medium" default is xSmall. | ||
* @param variant - string (optional) - Specifies the variant or shape of the skeleton. Should be one of "circle" | "rectangle" | "square" | "text", default is text. | ||
* @param fluid - boolean (optional) - Expands the skeleton element width to the width of the parent | ||
* | ||
* @returns JSX.Element - A skeleton loading element with the specified type, variant, and width (if provided). | ||
*/ | ||
export const Skeleton: FC<SkeletonProps & { style?: CSSProperties }> = ({ | ||
width, | ||
height, | ||
size, | ||
variant, | ||
style, | ||
fluid, | ||
}) => { | ||
return ( | ||
<FusionSkeleton | ||
size={size ? skeletonSize[size] : skeletonSize.xSmall} | ||
variant={variant ? skeletonVariant[variant] : skeletonVariant.text} | ||
style={{ | ||
...style, | ||
...(width && { width: typeof width === 'number' ? `${height}%` : height }), | ||
...(height && { height: typeof height === 'number' ? `${height}%` : height }), | ||
}} | ||
fluid={fluid} | ||
/> | ||
); | ||
}; |
152 changes: 152 additions & 0 deletions
152
client/apps/project-portal-landingpage/src/components/user/UserCard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import { Card, Typography, Icon } from '@equinor/eds-core-react'; | ||
|
||
import { tokens } from '@equinor/eds-tokens'; | ||
import styled from 'styled-components'; | ||
|
||
import { external_link, tag_relations } from '@equinor/eds-icons'; | ||
|
||
import { useMemo } from 'react'; | ||
import { useCurrentUser } from '../../hooks/user'; | ||
import { useRelationsByType } from '../../context'; | ||
import { PersonPosition } from '../../types/person-details'; | ||
import { ProfileCardHeader } from '../ProfileCardHeader'; | ||
import { useFrameworkCurrentContext } from '@equinor/fusion-framework-react-app/context'; | ||
|
||
declare global { | ||
interface Window { | ||
_config_: { | ||
fusionLegacyEnvIdentifier: string; | ||
}; | ||
} | ||
} | ||
|
||
export const getFusionPortalURL = () => { | ||
switch (window._config_.fusionLegacyEnvIdentifier.toLowerCase()) { | ||
case 'fprd': | ||
return 'https://fusion.equinor.com'; | ||
case 'ci': | ||
return 'https://fusion-s-portal-ci.azurewebsites.net'; | ||
case 'fqa': | ||
return 'https://fusion-s-portal-fqa.azurewebsites.net'; | ||
default: | ||
return 'https://fusion-s-portal-ci.azurewebsites.net'; | ||
} | ||
}; | ||
|
||
const Style = { | ||
Wrapper: styled.div` | ||
display: flex; | ||
flex-direction: column; | ||
gap: ${tokens.spacings.comfortable.medium}; | ||
padding: ${tokens.spacings.comfortable.medium}; | ||
`, | ||
PositionWrapper: styled.span` | ||
display: flex; | ||
flex-direction: column; | ||
`, | ||
ProjectHeader: styled.div` | ||
display: flex; | ||
justify-content: space-between; | ||
border-bottom: 1px solid ${tokens.colors.ui.background__medium.hex}; | ||
padding: ${tokens.spacings.comfortable.small}; | ||
`, | ||
PositionLink: styled.a` | ||
height: auto; | ||
border: 1px solid ${tokens.colors.ui.background__medium.hex}; | ||
color: ${tokens.colors.interactive.primary__resting.hex}; | ||
border-radius: 4px; | ||
width: 100%; | ||
text-decoration: none; | ||
:hover { | ||
background-color: ${tokens.colors.interactive.primary__hover_alt.hex}; | ||
} | ||
:focus { | ||
background-color: ${tokens.colors.interactive.primary__hover_alt.hex}; | ||
} | ||
`, | ||
|
||
Content: styled.div` | ||
padding: ${tokens.spacings.comfortable.medium_small}; | ||
display: flex; | ||
align-items: center; | ||
height: auto; | ||
`, | ||
Icon: styled(Icon)` | ||
padding-right: 1rem; | ||
`, | ||
}; | ||
|
||
export const ProjectPosition = ({ positions }: { positions?: PersonPosition[] }) => { | ||
const { currentContext } = useFrameworkCurrentContext(); | ||
const { data: equinorTask } = useRelationsByType('OrgChart', currentContext?.id); | ||
|
||
const projectPositions = useMemo(() => { | ||
return ( | ||
positions?.filter((item) => { | ||
return ( | ||
item.appliesTo && | ||
new Date(item.appliesTo) > new Date() && | ||
item.project.id === equinorTask[0]?.externalId | ||
); | ||
}) || [] | ||
); | ||
}, [positions, equinorTask]); | ||
|
||
return ( | ||
<> | ||
{ | ||
projectPositions.length > 0 ? ( | ||
<Style.Wrapper> | ||
{projectPositions.map((position) => ( | ||
<Style.PositionLink | ||
key={position.id} | ||
target="_blank" | ||
aria-label={position.name} | ||
href={`${getFusionPortalURL()}/apps/pro-org/${position.project.id}/chart/${ | ||
position.parentPositionId | ||
}`} | ||
role="link" | ||
> | ||
<Style.PositionWrapper> | ||
<Style.ProjectHeader> | ||
<Typography>{position.project.name}</Typography> | ||
<Icon data={external_link} size={16} /> | ||
</Style.ProjectHeader> | ||
<Style.Content> | ||
<Style.Icon data={tag_relations} /> | ||
<div> | ||
<Typography color={tokens.colors.interactive.primary__resting.hex}> | ||
{position.name} | ||
</Typography> | ||
<Typography> | ||
<> | ||
{position.appliesFrom && | ||
new Date(position.appliesFrom).toLocaleDateString('en-US')} | ||
{' - '} | ||
{position.appliesTo && | ||
new Date(position.appliesTo).toLocaleDateString('en-US')} | ||
({position.workload}%) | ||
</> | ||
</Typography> | ||
</div> | ||
</Style.Content> | ||
</Style.PositionWrapper> | ||
</Style.PositionLink> | ||
))} | ||
</Style.Wrapper> | ||
) : null //<Message title="You have no allocation for the selected project " /> | ||
} | ||
</> | ||
); | ||
}; | ||
|
||
export const User = () => { | ||
const user = useCurrentUser(); | ||
|
||
return ( | ||
<Card elevation="raised"> | ||
<ProfileCardHeader user={user.data} trigger="click" /> | ||
<ProjectPosition positions={user.data?.positions} /> | ||
</Card> | ||
); | ||
}; |
21 changes: 21 additions & 0 deletions
21
client/apps/project-portal-landingpage/src/context/hooks/use-relations-by-type.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { useEffect, useMemo, useState } from 'react'; | ||
import { RelationsTypes } from '../types/relations'; | ||
import { useContextRelationsQuery } from '../queries/get-context-relations'; | ||
|
||
export function useRelationsByType<RT extends RelationsTypes>(type: RT, contextId?: string) { | ||
const [error, setError] = useState<Error | undefined>(); | ||
const { data, isLoading } = useContextRelationsQuery<RT>(contextId); | ||
|
||
const filteredRelations = useMemo(() => { | ||
setError(undefined); | ||
return data?.filter((relation) => relation.type.id === type) || []; | ||
}, [data]); | ||
|
||
useEffect(() => { | ||
if (!isLoading && filteredRelations?.length === 0) { | ||
setError(Error(`No context relation found for ${type}`)); | ||
} | ||
}, [isLoading, filteredRelations]); | ||
|
||
return { data: filteredRelations, isLoading, error }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './types/relations'; | ||
export { useRelationsByType } from './hooks/use-relations-by-type'; |
25 changes: 25 additions & 0 deletions
25
client/apps/project-portal-landingpage/src/context/queries/get-context-relations.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { useFramework } from '@equinor/fusion-framework-react'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
import { IHttpClient } from '@equinor/fusion-framework-module-http'; | ||
import { RelationReturnType, RelationsTypes } from '../types/relations'; | ||
|
||
export async function getContextRelations<RT extends RelationsTypes>( | ||
client: IHttpClient, | ||
contextId?: string, | ||
signal?: AbortSignal | ||
): Promise<RelationReturnType<RT>[] | undefined> { | ||
if (!contextId) return; | ||
const res = await client.fetch(`/contexts/${contextId}/relations`, { signal }); | ||
if (!res.ok) throw res; | ||
return (await res.json()) as RelationReturnType<RT>[]; | ||
} | ||
|
||
export const useContextRelationsQuery = <RT extends RelationsTypes>(contextId?: string) => { | ||
const client = useFramework().modules.serviceDiscovery.createClient('context'); | ||
|
||
return useQuery({ | ||
queryKey: ['context-relations', contextId], | ||
queryFn: async ({ signal }) => getContextRelations<RT>(await client, contextId, signal), | ||
enabled: Boolean(contextId), | ||
}); | ||
}; |
Oops, something went wrong.