-
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.
- Loading branch information
Showing
10 changed files
with
406 additions
and
7 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
13 changes: 13 additions & 0 deletions
13
client/apps/project-portal-landingpage/src/context/ContextProvider.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,13 @@ | ||
import { ContextProvider as FusionContextProvider } from '@equinor/fusion-react-context-selector'; | ||
import { ReactNode } from 'react'; | ||
import { useContextResolver } from './hooks/use-context-resolver'; | ||
|
||
interface PortalContextProviderProps { | ||
children: ReactNode; | ||
} | ||
|
||
export const ContextProvider = ({ children }: PortalContextProviderProps) => { | ||
const resolver = useContextResolver(['ProjectMaster', 'Facility']); | ||
|
||
return <FusionContextProvider resolver={resolver}>{children}</FusionContextProvider>; | ||
}; |
28 changes: 28 additions & 0 deletions
28
client/apps/project-portal-landingpage/src/context/ContextSelector.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,28 @@ | ||
import { useContextProvider } from '@equinor/fusion-framework-react-app/context'; | ||
import FusionContextSelector, { ContextResult, ContextSelectEvent } from '@equinor/fusion-react-context-selector'; | ||
import { NavigateFunction } from 'react-router-dom'; | ||
|
||
interface ContextSelectorProps { | ||
variant?: string; | ||
navigate?: NavigateFunction; | ||
} | ||
|
||
export const ContextSelector = ({ variant }: ContextSelectorProps) => { | ||
const contextProvider = useContextProvider(); | ||
|
||
return ( | ||
<FusionContextSelector | ||
id="context-selector" | ||
variant={variant} | ||
onSelect={(e: ContextSelectEvent) => { | ||
e.stopPropagation(); | ||
// sins this is a single select the will be the next context at index 0 | ||
const context = (e.nativeEvent.detail.selected as ContextResult)[0]; | ||
contextProvider.contextClient.setCurrentContext(context.id); | ||
}} | ||
value={contextProvider.currentContext?.id ? contextProvider.currentContext?.title || '' : ''} | ||
placeholder="Start to type to search..." | ||
selectTextOnFocus={true} | ||
/> | ||
); | ||
}; |
61 changes: 61 additions & 0 deletions
61
client/apps/project-portal-landingpage/src/context/PortalContextSelector.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,61 @@ | ||
import { Button } from '@equinor/eds-core-react'; | ||
|
||
import { useNavigate } from 'react-router-dom'; | ||
import styled from 'styled-components'; | ||
|
||
import { ContextSelector } from './ContextSelector'; | ||
import { useContextProvider } from '@equinor/fusion-framework-react-app/context'; | ||
import { getContextPageURL } from '../hooks/utils'; | ||
import { NavigationModule } from '@equinor/fusion-framework-module-navigation'; | ||
import { useFramework } from '@equinor/fusion-framework-react'; | ||
|
||
const StyledWrapper = styled.div` | ||
display: flex; | ||
width: 50vw; | ||
flex-direction: row; | ||
justify-content: flex-start; | ||
align-items: center; | ||
gap: 0.5rem; | ||
> fwc-searchable-dropdown-provider { | ||
flex: 1; | ||
} | ||
@media only screen and (max-width: 60rem) { | ||
width: 80vw; | ||
} | ||
@media only screen and (max-width: 45rem) { | ||
width: 90vw; | ||
flex-direction: column; | ||
} | ||
`; | ||
|
||
const StyledButton = styled(Button)` | ||
white-space: nowrap; | ||
`; | ||
const StyledActionWrapper = styled.div` | ||
min-width: 120px; | ||
`; | ||
|
||
export const PortalContextSelector = () => { | ||
const { currentContext } = useContextProvider(); | ||
const { modules } = useFramework<[NavigationModule]>(); | ||
|
||
return ( | ||
<StyledWrapper> | ||
<ContextSelector /> | ||
<StyledActionWrapper> | ||
{currentContext && ( | ||
<StyledButton | ||
variant="ghost" | ||
onClick={() => { | ||
modules.navigation.replace(getContextPageURL(currentContext)); | ||
}} | ||
> | ||
Go to {currentContext.title} | ||
</StyledButton> | ||
)} | ||
</StyledActionWrapper> | ||
</StyledWrapper> | ||
); | ||
}; |
22 changes: 22 additions & 0 deletions
22
client/apps/project-portal-landingpage/src/context/hooks/use-context-client.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,22 @@ | ||
import { useEffect, useState } from 'react'; | ||
|
||
import { | ||
ClientMethodType, | ||
ServicesModule, | ||
} from '@equinor/fusion-framework-module-services'; | ||
import { ContextApiClient } from '@equinor/fusion-framework-module-services/context'; | ||
import { useFramework } from '@equinor/fusion-framework-react'; | ||
|
||
export const useContextClient = <T extends ClientMethodType>( | ||
type: T | ||
): ContextApiClient<T> | null => { | ||
const [client, setClient] = useState<ContextApiClient<T> | null>(null); | ||
|
||
const { modules } = useFramework<[ServicesModule]>(); | ||
|
||
useEffect(() => { | ||
modules.services.createContextClient(type).then(setClient); | ||
}, [modules.services, setClient, type]); | ||
|
||
return client; | ||
}; |
128 changes: 128 additions & 0 deletions
128
client/apps/project-portal-landingpage/src/context/hooks/use-context-resolver.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,128 @@ | ||
import { QueryContextResponse } from '@equinor/fusion-framework-module-services/context/query'; | ||
import { ContextResolver, ContextResult, ContextResultItem } from '@equinor/fusion-react-context-selector'; | ||
import { useCallback } from 'react'; | ||
|
||
// import { clearLocalContext } from '../framework-configurator'; | ||
import { useContextProvider } from '@equinor/fusion-framework-react-app/context'; | ||
import { getContextHistory } from '../portal-context-history'; | ||
import { useContextClient } from './use-context-client'; | ||
|
||
export const useContextResolver = (type: string[]): ContextResolver => { | ||
const contextProvider = useContextProvider(); | ||
|
||
const client = useContextClient('json'); | ||
const minQueryLength = 2; | ||
|
||
const searchQuery = useCallback( | ||
async (search: string): Promise<ContextResult> => { | ||
let searchResult: ContextResult = []; | ||
if (!client) { | ||
return [ | ||
singleItem({ | ||
title: 'Client Error', | ||
subTitle: 'No client provided to framework', | ||
isDisabled: true, | ||
isError: true, | ||
}), | ||
]; | ||
} | ||
|
||
try { | ||
if (!search || search.length < minQueryLength) { | ||
searchResult.push( | ||
singleItem({ | ||
title: `Need ${minQueryLength - search.length} more chars`, | ||
isDisabled: true, | ||
}) | ||
); | ||
return searchResult; | ||
} | ||
|
||
const contexts = await client.query('v1', { | ||
query: { search, filter: { type } }, | ||
}); | ||
|
||
if (contexts[0] && !contexts[0].id) return searchResult; | ||
// Structure as type | ||
|
||
searchResult = type.length > 1 ? contextResultMappedByTypes(contexts) : contextResultMapped(contexts); | ||
|
||
if (searchResult.length === 0) { | ||
searchResult.push(singleItem({ title: 'No matches...', isDisabled: true })); | ||
} | ||
|
||
return searchResult; | ||
} catch (e) { | ||
return [ | ||
singleItem({ | ||
title: 'API Error', | ||
subTitle: e, | ||
isDisabled: true, | ||
isError: true, | ||
}), | ||
]; | ||
} | ||
}, | ||
[client, type] | ||
); | ||
|
||
const children = getContextHistory(type); | ||
|
||
const historyItems = { | ||
id: 'history', | ||
title: 'History', | ||
type: 'section', | ||
children, | ||
}; | ||
|
||
return { | ||
searchQuery, | ||
initialResult: children.length > 0 ? [singleItem(historyItems)] : [], | ||
closeHandler: (e: MouseEvent) => { | ||
e.stopPropagation(); | ||
contextProvider.clearCurrentContext(); | ||
// clearLocalContext(); | ||
}, | ||
}; | ||
}; | ||
|
||
const singleItem = (props: unknown): ContextResultItem => { | ||
return Object.assign({ id: '0', title: 'Dummy title' }, props); | ||
}; | ||
|
||
function contextResultMappedByTypes(contexts: QueryContextResponse<'v1'>): ContextResult { | ||
return contexts.reduce((result, context) => { | ||
const index = result.findIndex((r) => r.title === context.type.id); | ||
if (index === -1) { | ||
result.push( | ||
singleItem({ | ||
id: context.type.id, | ||
title: context.type.id, | ||
type: 'section', | ||
children: [singleItem({ id: context.id, title: context.title || '', subTitle: context.type?.id })], | ||
}) | ||
); | ||
return result; | ||
} | ||
|
||
result[index].children?.push( | ||
singleItem({ | ||
id: context.id, | ||
title: context.title || '', | ||
subTitle: context.type?.id, | ||
}) | ||
); | ||
|
||
return result; | ||
}, [] as ContextResult); | ||
} | ||
|
||
function contextResultMapped(contexts: QueryContextResponse<'v1'>): ContextResult { | ||
return contexts.map((context) => | ||
singleItem({ | ||
id: context.id, | ||
title: context.title || '', | ||
subTitle: context.type?.id, | ||
}) | ||
); | ||
} |
48 changes: 48 additions & 0 deletions
48
client/apps/project-portal-landingpage/src/context/portal-context-configurators.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,48 @@ | ||
import { IContextProvider } from '@equinor/fusion-framework-module-context'; | ||
|
||
import { setContextHistory } from './portal-context-history'; | ||
import { storage } from '../utils/local-storage'; | ||
|
||
const CONTEXT_SOCAGE_KEY = 'context'; | ||
|
||
export function storeCurrentContext(contextProvider: IContextProvider) { | ||
contextProvider.currentContext$.subscribe((context) => { | ||
if (context?.title === 'Unexpected error' || !context?.id || !context) { | ||
return; | ||
} | ||
|
||
const storedContextId = storage.getItem<string>(CONTEXT_SOCAGE_KEY); | ||
// Update the history with the current context selected. | ||
setContextHistory(context); | ||
|
||
if (context.id !== storedContextId) { | ||
storage.setItem(CONTEXT_SOCAGE_KEY, context?.id); | ||
} | ||
}); | ||
} | ||
|
||
export function clearLocalContext() { | ||
storage.removeItem(CONTEXT_SOCAGE_KEY); | ||
} | ||
|
||
export function validateLocalContext(contextId: string): boolean { | ||
const storedContextId = storage.getItem<string>(CONTEXT_SOCAGE_KEY); | ||
return contextId === storedContextId; | ||
} | ||
|
||
export function setStoredContext(contextProvider: IContextProvider) { | ||
const storedContextId = storage.getItem<string>(CONTEXT_SOCAGE_KEY); | ||
|
||
const uriContext = getContextFormUrl(); | ||
|
||
if (contextProvider.currentContext?.id !== storedContextId || uriContext) { | ||
contextProvider.contextClient.setCurrentContext(uriContext ? uriContext : storedContextId); | ||
} | ||
} | ||
|
||
export function getContextFormUrl() { | ||
const match = window.location.pathname.match( | ||
/[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/ | ||
); | ||
return match ? match[0] : undefined; | ||
} |
Oops, something went wrong.