diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaField/useIdeaField.tsx b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaField/useIdeaField.tsx index 3e6ab01e4..9a8a30d48 100644 --- a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaField/useIdeaField.tsx +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaField/useIdeaField.tsx @@ -28,7 +28,10 @@ export function useIdeaField(props: IIdeaFieldProps) { const renderValueForField = () => { let icon = TagMultipleFilled - if (props.model.internalName === 'GtIdeaRecommendation') { + if ( + props.model.internalName === 'GtIdeaRecommendation' || + props.model.internalName === 'GtIdeaDecision' + ) { props.model.type = 'TaxonomyFieldType' } diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaModule.module.scss b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaModule.module.scss index 56f72be66..32a4265b3 100644 --- a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaModule.module.scss +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaModule.module.scss @@ -2,25 +2,33 @@ margin: 0; overflow: hidden; display: flex; - height: 100%; + height: 100vh; .content { flex: 1; - padding: 16px; + padding: 32px; gap: 16px; display: grid; justify-content: flex-start; align-items: flex-start; + height: fit-content; .ideaHeader { display: flex; flex-direction: column; gap: 16px; - .ideaPhases { + .toolbar { display: flex; - flex-direction: row; + flex-direction: column; justify-content: center; + align-items: center; + gap: 16px; + + .hamburger { + position: absolute; + left: 32px; + } } .ideaTitle { @@ -39,6 +47,13 @@ .ideaField { margin: 0; } + + .status { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 8px; + } } } } diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaModule.tsx b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaModule.tsx index 992cc5872..9d710cced 100644 --- a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaModule.tsx +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaModule.tsx @@ -3,56 +3,14 @@ import styles from './IdeaModule.module.scss' import { IdeaModuleContext } from './context' import { IIdeaModuleProps } from './types' import { useIdeaModule } from './useIdeaModule' -import { - Divider, - FluentProvider, - IdPrefixProvider, - Spinner, - Tab, - TabList, - Tooltip -} from '@fluentui/react-components' -import { customLightTheme, getFluentIcon, setUrlHash, UserMessage } from 'pp365-shared-library' -import { - Hamburger, - NavCategory, - NavCategoryItem, - NavDrawer, - NavDrawerBody, - NavDrawerHeader, - NavItem, - NavSectionHeader, - NavSubItem, - NavSubItemGroup, - NavDivider, - AppItemStatic -} from '@fluentui/react-nav-preview' -import { - Board20Filled, - Board20Regular, - NotePin20Filled, - NotePin20Regular, - Lightbulb20Filled, - Lightbulb20Regular, - bundleIcon -} from '@fluentui/react-icons' +import { Divider, FluentProvider, IdPrefixProvider, Spinner } from '@fluentui/react-components' +import { customLightTheme, UserMessage } from 'pp365-shared-library' import { IdeaField } from './IdeaField' - -const Dashboard = bundleIcon(Board20Filled, Board20Regular) -const Lightbulb = bundleIcon(Lightbulb20Filled, Lightbulb20Regular) -const JobPostings = bundleIcon(NotePin20Filled, NotePin20Regular) +import { IdeaPhaseBar } from './IdeaPhaseBar' +import { IdeaNav } from './IdeaNav' export const IdeaModule: FC = (props) => { - const { state, setState, getSelectedIdea, fluentProviderId } = useIdeaModule(props) - const [isOpen, setIsOpen] = React.useState(true) - - const renderHamburgerWithToolTip = () => { - return ( - - setIsOpen(!isOpen)} /> - - ) - } + const { state, setState, isOpen, renderHamburger, fluentProviderId } = useIdeaModule(props) return ( @@ -62,73 +20,7 @@ export const IdeaModule: FC = (props) => { ) : (
- - - - {renderHamburgerWithToolTip()} - - - - Idémodul - } value='total'> - Totaloversikt - - Registrering - } value='registrering'> - Oversikt - - - }>Mine idéer - - {state.ideas.data.items - .filter((idea) => !idea.processing) - .map((idea) => ( - { - setUrlHash({ ideaId: idea.Id.toString() }) - getSelectedIdea() - }} - > - {idea?.Title} - - ))} - - - - Behandling - } value='behandling'> - Oversikt - - - }>Mine idéer - - {state.ideas.data.items - .filter((idea) => idea.processing) - .map((idea) => ( - { - setUrlHash({ ideaId: idea.Id.toString() }) - getSelectedIdea() - }} - > - {idea?.Title} - - ))} - - - - +
{state.error && ( = (props) => { {state.selectedIdea ? ( <>
- - - Registrering av idé - - - - Godkjent for behandling - - - - Behandling av idé - - - - Idé godkjent - - - - Bestill prosjekt - - +
+
{!isOpen && renderHamburger()}
+ +

{state.selectedIdea.item.Title}

@@ -222,7 +53,6 @@ export const IdeaModule: FC = (props) => { ) : ( )} - {!isOpen && renderHamburgerWithToolTip()}
)} diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaNav/IdeaNav.tsx b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaNav/IdeaNav.tsx new file mode 100644 index 000000000..e293108d6 --- /dev/null +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaNav/IdeaNav.tsx @@ -0,0 +1,106 @@ +import React, { FC } from 'react' +import { + NavCategory, + NavCategoryItem, + NavDrawer, + NavDrawerBody, + NavDrawerHeader, + NavItem, + NavSectionHeader, + NavSubItem, + NavSubItemGroup, + NavDivider, + AppItemStatic +} from '@fluentui/react-nav-preview' +import { + Board20Filled, + Board20Regular, + NotePin20Filled, + NotePin20Regular, + Lightbulb20Filled, + Lightbulb20Regular, + bundleIcon +} from '@fluentui/react-icons' +import { useIdeaModuleContext } from '../context' +import { Tooltip } from '@fluentui/react-components' +import { getFluentIcon, setUrlHash } from 'pp365-shared-library' +import { useIdeaModule } from '../useIdeaModule' + +const Dashboard = bundleIcon(Board20Filled, Board20Regular) +const Lightbulb = bundleIcon(Lightbulb20Filled, Lightbulb20Regular) +const JobPostings = bundleIcon(NotePin20Filled, NotePin20Regular) + +export const IdeaNav: FC = () => { + const context = useIdeaModuleContext() + const { getSelectedIdea, isOpen, renderHamburger } = useIdeaModule() + + return ( + + + + {renderHamburger()} + + + + Idémodul + } value='total'> + Totaloversikt + + Registrering + } value='registrering'> + Oversikt + + + }>Mine idéer + + {context.state.ideas.data.items + .filter((idea) => !idea.processing) + .map((idea) => ( + { + setUrlHash({ ideaId: idea.Id.toString() }) + getSelectedIdea() + }} + > + {idea?.Title} + + ))} + + + + Behandling + } value='behandling'> + Oversikt + + + }>Mine idéer + + {context.state.ideas.data.items + .filter((idea) => idea.processing) + .map((idea) => ( + { + setUrlHash({ ideaId: idea.Id.toString() }) + getSelectedIdea() + }} + > + {idea?.Title} + + ))} + + + + + ) +} diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaNav/index.ts b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaNav/index.ts new file mode 100644 index 000000000..767364809 --- /dev/null +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaNav/index.ts @@ -0,0 +1 @@ +export * from './IdeaNav' diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/IdeaPhaseBar.module.scss b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/IdeaPhaseBar.module.scss new file mode 100644 index 000000000..98f8292ca --- /dev/null +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/IdeaPhaseBar.module.scss @@ -0,0 +1,6 @@ +.ideaPhases { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center +} diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/IdeaPhaseBar.tsx b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/IdeaPhaseBar.tsx new file mode 100644 index 000000000..31ac5eac1 --- /dev/null +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/IdeaPhaseBar.tsx @@ -0,0 +1,77 @@ +import React, { FC } from 'react' +import styles from './IdeaPhaseBar.module.scss' +import { Tab, TabList } from '@fluentui/react-components' +import { getFluentIcon } from 'pp365-shared-library' +import { useIdeaModuleContext } from '../context' +import { IdeaPhase } from '../types' +import { ProjectProvision } from 'components/ProjectProvision' + +export const IdeaPhaseBar: FC = () => { + const context = useIdeaModuleContext() + + const phases = [ + { + phase: IdeaPhase.Registration, + name: 'Registrering av idé', + icon: getFluentIcon('Lightbulb') + }, + { + phase: IdeaPhase.Processing, + name: 'Behandling av idé', + icon: getFluentIcon('Edit') + }, + { + phase: IdeaPhase.ApprovedForConcept, + name: 'Idé godkjent', + icon: getFluentIcon('CheckmarkCircle') + }, + { + phase: IdeaPhase.Provisioned, + name: 'Bestill område', + icon: getFluentIcon('BoxToolbox') + } + ] + return ( + <> + + {phases.map((phase) => { + if (phase.phase === IdeaPhase.Provisioned) { + return ( + + ) + } + + return ( + <> + + {phase.name} + + + + ) + })} + + + ) +} diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/index.ts b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/index.ts new file mode 100644 index 000000000..b7140167b --- /dev/null +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/IdeaPhaseBar/index.ts @@ -0,0 +1 @@ +export * from './IdeaPhaseBar' diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/types.ts b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/types.ts index ea6339121..1ac0007e3 100644 --- a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/types.ts +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/types.ts @@ -7,6 +7,7 @@ import { import { IBaseComponentProps } from '../types' import { MessageBarType } from '@fluentui/react' import { IdeaConfigurationModel } from 'models' +import { Slot } from '@fluentui/react-components' export class IdeaModuleErrorMessage extends Error { constructor(public message: string, public type: MessageBarType) { @@ -35,6 +36,7 @@ export interface IIdeaModuleState { configuration?: IdeaConfigurationModel ideas?: Idea selectedIdea?: IIdea + phase?: IdeaPhase searchTerm: string renderMode?: IdeaListRenderMode isUserInIdeaManagerGroup?: boolean @@ -95,3 +97,16 @@ export interface IIdeasData { */ columns: ProjectContentColumn[] } + +export interface IIdeaPhase { + phase: IdeaPhase + name: string + icon?: Slot<'span'> +} + +export enum IdeaPhase { + Registration, + Processing, + ApprovedForConcept, + Provisioned +} diff --git a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/useIdeaModule.ts b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/useIdeaModule.tsx similarity index 71% rename from SharePointFramework/PortfolioWebParts/src/components/IdeaModule/useIdeaModule.ts rename to SharePointFramework/PortfolioWebParts/src/components/IdeaModule/useIdeaModule.tsx index a8dd51763..da319dfa1 100644 --- a/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/useIdeaModule.ts +++ b/SharePointFramework/PortfolioWebParts/src/components/IdeaModule/useIdeaModule.tsx @@ -1,23 +1,34 @@ /* eslint-disable no-console */ - -import { useId } from '@fluentui/react-components' -import { IIdeaModuleHashState, IIdeaModuleProps } from './types' +import React from 'react' +import { Tooltip, useId } from '@fluentui/react-components' +import { IdeaPhase, IIdeaModuleHashState, IIdeaModuleProps } from './types' import { useIdeaModuleState } from './useIdeaModuleState' import { useIdeaModuleDataFetch } from './useIdeaModuleDataFetch' import { EditableSPField, ItemFieldValues, parseUrlHash, setUrlHash } from 'pp365-shared-library' import _ from 'lodash' -import { useEffect } from 'react' +import { useEffect, useState } from 'react' +import { Hamburger } from '@fluentui/react-nav-preview' /** * Component logic hook for `IdeaModule` component. * clear */ -export function useIdeaModule(props: IIdeaModuleProps) { +export function useIdeaModule(props?: IIdeaModuleProps) { const { state, setState } = useIdeaModuleState(props) const fluentProviderId = useId('fp-idea-module') useIdeaModuleDataFetch(props, state.refetch, setState) + const [isOpen, setIsOpen] = useState(true) + + const renderHamburger = () => { + return ( + + setIsOpen(!isOpen)} /> + + ) + } + const getSelectedIdea = () => { const hashState = parseUrlHash() @@ -78,13 +89,35 @@ export function useIdeaModule(props: IIdeaModuleProps) { return a.column.sortOrder - b.column.sortOrder }) + let ideaPhase: IdeaPhase + if (selectedIdea.processing) { + if ( + selectedIdea.processing.GtIdeaDecision === + state.configuration.processing.find((p) => p.key === 'approve')?.recommendation + ) { + ideaPhase = IdeaPhase.ApprovedForConcept + } else { + ideaPhase = IdeaPhase.Processing + } + } else { + if ( + selectedIdea.GtIdeaRecommendation === + state.configuration.registration.find((p) => p.key === 'approve')?.recommendation + ) { + ideaPhase = IdeaPhase.Processing + } else { + ideaPhase = IdeaPhase.Registration + } + } + setState({ ...state, selectedIdea: { item: selectedIdea, registeredFieldValues, processingFieldValues - } + }, + phase: ideaPhase }) } @@ -98,6 +131,8 @@ export function useIdeaModule(props: IIdeaModuleProps) { state, setState, getSelectedIdea, + renderHamburger, + isOpen, fluentProviderId } } diff --git a/SharePointFramework/PortfolioWebParts/src/components/ProjectProvision/ProjectProvision.tsx b/SharePointFramework/PortfolioWebParts/src/components/ProjectProvision/ProjectProvision.tsx index 4365cc16a..462aaed96 100644 --- a/SharePointFramework/PortfolioWebParts/src/components/ProjectProvision/ProjectProvision.tsx +++ b/SharePointFramework/PortfolioWebParts/src/components/ProjectProvision/ProjectProvision.tsx @@ -51,9 +51,10 @@ export const ProjectProvision: FC = (props) => { primaryActionButton={{ onClick: () => setState({ showProvisionDrawer: true }) }} - icon={getFluentIcon('Add')} - appearance='primary' - size='large' + icon={props.icon} + appearance={props.appearance} + size={props.size} + disabled={props.disabled} > {strings.Provision.ProvisionButtonLabel} @@ -88,3 +89,10 @@ export const ProjectProvision: FC = (props) => { ) } + +ProjectProvision.defaultProps = { + disabled: false, + icon: getFluentIcon('Add'), + appearance: 'primary', + size: 'large' +} diff --git a/SharePointFramework/PortfolioWebParts/src/components/ProjectProvision/types.ts b/SharePointFramework/PortfolioWebParts/src/components/ProjectProvision/types.ts index 2443c10bf..ac9054484 100644 --- a/SharePointFramework/PortfolioWebParts/src/components/ProjectProvision/types.ts +++ b/SharePointFramework/PortfolioWebParts/src/components/ProjectProvision/types.ts @@ -1,7 +1,12 @@ +import { Slot } from '@fluentui/react-components' import { IBaseComponentProps } from 'components/types' export interface IProjectProvisionProps extends IBaseComponentProps { provisionUrl: string + disabled?: boolean + icon?: Slot<'span'> + appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent' + size?: 'small' | 'medium' | 'large' } export interface IProjectProvisionState { diff --git a/SharePointFramework/shared-library/src/util/createFieldValueMap.ts b/SharePointFramework/shared-library/src/util/createFieldValueMap.ts index ea3aa2f6a..e415409f4 100644 --- a/SharePointFramework/shared-library/src/util/createFieldValueMap.ts +++ b/SharePointFramework/shared-library/src/util/createFieldValueMap.ts @@ -21,7 +21,7 @@ export const createFieldValueMap = (): Map value.split(';').map((v) => ({ key: v, name: v }))], + ['TaxonomyFieldType', ({ value }) => value?.split(';').map((v) => ({ key: v, name: v }))], [ 'TaxonomyFieldTypeMulti', ({ $ }) => @@ -32,7 +32,7 @@ export const createFieldValueMap = (): Map new Date($)], ['DateTime', ({ $ }) => new Date($)], - ['MultiChoice', ({ value }) => value.split(', ')], + ['MultiChoice', ({ value }) => value?.split(', ')], [ 'User', ({ $ }) => diff --git a/Templates/Portfolio/Objects/Lists/Prosjektinnholdskolonner.xml b/Templates/Portfolio/Objects/Lists/Prosjektinnholdskolonner.xml index 8c15236dd..7e6b5ffe4 100644 --- a/Templates/Portfolio/Objects/Lists/Prosjektinnholdskolonner.xml +++ b/Templates/Portfolio/Objects/Lists/Prosjektinnholdskolonner.xml @@ -842,17 +842,6 @@ 1080 - Andre kommentarer - GtIdeaOtherComments - GtIdeaOtherCommentsOWSMTXT - Note - Idémodul - 200 - 300 - True - - - 1090 Innmelder GtIdeaReporter GtIdeaReporter @@ -864,7 +853,7 @@ True - 1100 + 1090 Kommentar til anbefaling GtIdeaRecommendationComment GtIdeaRecommendationCommentOWSMTXT @@ -875,7 +864,7 @@ True - 1110 + 1100 Anbefaling GtIdeaRecommendation GtIdeaRecommendationOWSCHCS @@ -887,7 +876,7 @@ True - 1120 + 1110 Idéeier GtIdeaOwner GtIdeaOwner @@ -898,7 +887,7 @@ True - 1130 + 1120 Overordnet løsningsbeskrivelse GtIdeaSolutionDescription GtIdeaSolutionDescriptionOWSMTXT @@ -908,7 +897,7 @@ 300 - 1140 + 1130 Forventede gevinster GtIdeaExpectedGain GtIdeaExpectedGainOWSMTXT @@ -918,7 +907,7 @@ 300 - 1150 + 1140 Gjennomføringsplan: Tilnærming GtIdeaExecutionPlanApproach GtIdeaExecutionPlanApproachOWSMTXT @@ -928,7 +917,7 @@ 300 - 1160 + 1150 Gjennomføringsplan: Ressursbehov GtIdeaExecutionResourceNeeds GtIdeaExecutionResourceNeedsOWSMTXT @@ -938,7 +927,7 @@ 300 - 1170 + 1160 Gjennomføringsplan: Suksessfaktorer GtIdeaExecutionSuccessFactors GtIdeaExecutionSuccessFactorsOWSMTXT @@ -948,7 +937,7 @@ 300 - 1180 + 1170 Referanse GtIdeaReference GtIdeaReferenceOWSMTXT @@ -957,6 +946,17 @@ 200 300 + + 1180 + Andre kommentarer + GtIdeaOtherComments + GtIdeaOtherCommentsOWSMTXT + Note + Idémodul + 200 + 300 + True + 1190 Beslutning