diff --git a/src/components/presentation/AppHeader/AppHeaderMenu.tsx b/src/components/presentation/AppHeader/AppHeaderMenu.tsx index b54cf2f361..63a551cfaf 100644 --- a/src/components/presentation/AppHeader/AppHeaderMenu.tsx +++ b/src/components/presentation/AppHeader/AppHeaderMenu.tsx @@ -8,7 +8,7 @@ import { CircleIcon } from 'src/components/CircleIcon'; import classes from 'src/components/presentation/AppHeader/AppHeaderMenu.module.css'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; -import { useCurrentParty, useInstanceOwnerParty } from 'src/features/party/PartiesProvider'; +import { useInstanceOwnerParty, useSelectedParty } from 'src/features/party/PartiesProvider'; import { useProfile } from 'src/features/profile/ProfileProvider'; import { useIsMobile } from 'src/hooks/useDeviceWidths'; import { logoutUrlAltinn } from 'src/utils/urls/urlHelper'; @@ -85,7 +85,7 @@ export function AppHeaderMenu({ logoColor }: AppHeaderMenuProps) { */ function useGetOnBehalfOf() { const instanceOwnerParty = useInstanceOwnerParty(); - const selectedParty = useCurrentParty(); + const selectedParty = useSelectedParty(); const userParty = useProfile()?.party; const onBehalfOfParty = instanceOwnerParty ?? selectedParty; diff --git a/src/components/presentation/BackNavigationButton.tsx b/src/components/presentation/BackNavigationButton.tsx index dfa478fb57..36c638c475 100644 --- a/src/components/presentation/BackNavigationButton.tsx +++ b/src/components/presentation/BackNavigationButton.tsx @@ -11,7 +11,7 @@ import { useAppQueries } from 'src/core/contexts/AppQueriesProvider'; import { useIsProcessing } from 'src/core/contexts/processingContext'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useSelectedParty } from 'src/features/party/PartiesProvider'; import { useIsSubformPage, useNavigationParam } from 'src/features/routing/AppRoutingContext'; import { useIsMobile } from 'src/hooks/useDeviceWidths'; import { useNavigatePage } from 'src/hooks/useNavigatePage'; @@ -20,7 +20,7 @@ import { getMessageBoxUrl } from 'src/utils/urls/urlHelper'; export function BackNavigationButton(props: Parameters[0]) { const { langAsString } = useLanguage(); const isMobile = useIsMobile(); - const party = useCurrentParty(); + const party = useSelectedParty(); const mainPageKey = useNavigationParam('mainPageKey'); const isSubform = useIsSubformPage(); const { returnUrl, isFetchingReturnUrl } = useReturnUrl(); diff --git a/src/features/entrypoint/Entrypoint.tsx b/src/features/entrypoint/Entrypoint.tsx index c310e908bc..44de30a8fc 100644 --- a/src/features/entrypoint/Entrypoint.tsx +++ b/src/features/entrypoint/Entrypoint.tsx @@ -6,9 +6,9 @@ import { FormProvider } from 'src/features/form/FormContext'; import { InstantiateContainer } from 'src/features/instantiate/containers/InstantiateContainer'; import { NoValidPartiesError } from 'src/features/instantiate/containers/NoValidPartiesError'; import { - useCurrentParty, - useCurrentPartyIsValid, useHasSelectedParty, + useSelectedParty, + useSelectedPartyIsValid, useValidParties, } from 'src/features/party/PartiesProvider'; import { useProfile } from 'src/features/profile/ProfileProvider'; @@ -45,10 +45,10 @@ export const Entrypoint = () => { } = useApplicationMetadata(); const profile = useProfile(); const validParties = useValidParties(); - const partyIsValid = useCurrentPartyIsValid(); + const partyIsValid = useSelectedPartyIsValid(); const userHasSelectedParty = useHasSelectedParty(); const allowAnonymous = useAllowAnonymousIs(true); - const party = useCurrentParty(); + const party = useSelectedParty(); if (isStateless && allowAnonymous && !party) { // Anonymous stateless app. No need to log in and select party, but cannot create a new instance. diff --git a/src/features/formData/FormDataWrite.tsx b/src/features/formData/FormDataWrite.tsx index 4038b6d79b..abd8195b93 100644 --- a/src/features/formData/FormDataWrite.tsx +++ b/src/features/formData/FormDataWrite.tsx @@ -21,7 +21,7 @@ import { ALTINN_ROW_ID } from 'src/features/formData/types'; import { getFormDataQueryKey } from 'src/features/formData/useFormDataQuery'; import { useLaxChangeInstance, useLaxInstanceId } from 'src/features/instance/InstanceContext'; import { useCurrentLanguage } from 'src/features/language/LanguageProvider'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useSelectedParty } from 'src/features/party/PartiesProvider'; import { type BackendValidationIssueGroups, IgnoredValidators } from 'src/features/validation'; import { useIsUpdatingInitialValidations } from 'src/features/validation/backendValidation/backendValidationQuery'; import { useAsRef } from 'src/hooks/useAsRef'; @@ -96,7 +96,7 @@ function useFormDataSaveMutation() { const cancelSave = useSelector((s) => s.cancelSave); const isStateless = useApplicationMetadata().isStatelessApp; const debounce = useSelector((s) => s.debounce); - const currentPartyId = useCurrentParty()?.partyId; + const selectedPartyId = useSelectedParty()?.partyId; const waitFor = useWaitForState< { prev: { [dataType: string]: object }; next: { [dataType: string]: object } }, FormDataContext @@ -203,9 +203,9 @@ function useFormDataSaveMutation() { if (isStateless) { const options: AxiosRequestConfig = {}; - if (currentPartyId !== undefined) { + if (selectedPartyId !== undefined) { options.headers = { - party: `partyid:${currentPartyId}`, + party: `partyid:${selectedPartyId}`, }; } diff --git a/src/features/formData/useFormDataQuery.tsx b/src/features/formData/useFormDataQuery.tsx index 4de0a29a56..940fc2debc 100644 --- a/src/features/formData/useFormDataQuery.tsx +++ b/src/features/formData/useFormDataQuery.tsx @@ -7,7 +7,7 @@ import type { AxiosRequestConfig } from 'axios'; import { useAppQueries } from 'src/core/contexts/AppQueriesProvider'; import { type QueryDefinition } from 'src/core/queries/usePrefetchQuery'; import { useApplicationMetadata } from 'src/features/applicationMetadata/ApplicationMetadataProvider'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useSelectedParty } from 'src/features/party/PartiesProvider'; import { useMemoDeepEqual } from 'src/hooks/useStateDeepEqual'; import { isAxiosError } from 'src/utils/isAxiosError'; import { maybeAuthenticationRedirect } from 'src/utils/maybeAuthenticationRedirect'; @@ -55,12 +55,12 @@ export async function invalidateFormDataQueries(queryClient: QueryClient) { } export function useFormDataQueryOptions() { - const currentPartyId = useCurrentParty()?.partyId; + const selectedPartyId = useSelectedParty()?.partyId; const isStateless = useApplicationMetadata().isStatelessApp; const options: AxiosRequestConfig = {}; - if (isStateless && currentPartyId !== undefined) { + if (isStateless && selectedPartyId !== undefined) { options.headers = { - party: `partyid:${currentPartyId}`, + party: `partyid:${selectedPartyId}`, }; } return options; diff --git a/src/features/instantiate/containers/InstantiateContainer.tsx b/src/features/instantiate/containers/InstantiateContainer.tsx index 76c71748b5..c33af0118c 100644 --- a/src/features/instantiate/containers/InstantiateContainer.tsx +++ b/src/features/instantiate/containers/InstantiateContainer.tsx @@ -5,7 +5,7 @@ import { InstantiateValidationError } from 'src/features/instantiate/containers/ import { MissingRolesError } from 'src/features/instantiate/containers/MissingRolesError'; import { UnknownError } from 'src/features/instantiate/containers/UnknownError'; import { useInstantiation } from 'src/features/instantiate/InstantiationContext'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useSelectedParty } from 'src/features/party/PartiesProvider'; import { useAsRef } from 'src/hooks/useAsRef'; import { AltinnPalette } from 'src/theme/altinnAppTheme'; import { changeBodyBackground } from 'src/utils/bodyStyling'; @@ -14,7 +14,7 @@ import { HttpStatusCodes } from 'src/utils/network/networking'; export const InstantiateContainer = () => { changeBodyBackground(AltinnPalette.greyLight); - const party = useCurrentParty(); + const party = useSelectedParty(); const instantiation = useInstantiation(); const clearRef = useAsRef(instantiation.clear); diff --git a/src/features/instantiate/containers/MissingRolesError.tsx b/src/features/instantiate/containers/MissingRolesError.tsx index 3ba78d8796..1196a11acb 100644 --- a/src/features/instantiate/containers/MissingRolesError.tsx +++ b/src/features/instantiate/containers/MissingRolesError.tsx @@ -4,12 +4,12 @@ import { Link } from 'react-router-dom'; import { InstantiationErrorPage } from 'src/features/instantiate/containers/InstantiationErrorPage'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useSelectedParty } from 'src/features/party/PartiesProvider'; import { getHostname } from 'src/utils/urls/appUrlHelper'; export function MissingRolesError() { const { langAsString } = useLanguage(); - const selectedParty = useCurrentParty(); + const selectedParty = useSelectedParty(); return ( {props.children}
{JSON.stringify(partyIsValid)}
-
{JSON.stringify(currentParty?.partyId ?? false)}
+
{JSON.stringify(selectedParty?.partyId ?? false)}
); } @@ -150,9 +150,9 @@ describe('PartySelection', () => { } await user.click(screen.getByRole('button', { name: partyName })); - await waitFor(() => expect(mutations.doSetCurrentParty.mock).toHaveBeenCalled()); - expect(mutations.doSetCurrentParty.mock).toHaveBeenCalledWith(expectedPartyId); - mutations.doSetCurrentParty.resolve('Party successfully updated'); + await waitFor(() => expect(mutations.doSetSelectedParty.mock).toHaveBeenCalled()); + expect(mutations.doSetSelectedParty.mock).toHaveBeenCalledWith(expectedPartyId); + mutations.doSetSelectedParty.resolve('Party successfully updated'); await waitFor(() => expect(screen.getByTestId('current-party')).toHaveTextContent(`${expectedPartyId}`)); await waitFor(() => expect(screen.getByTestId('valid-party')).toHaveTextContent('true')); }, diff --git a/src/features/instantiate/containers/PartySelection.tsx b/src/features/instantiate/containers/PartySelection.tsx index 2e65df4396..50f827eab4 100644 --- a/src/features/instantiate/containers/PartySelection.tsx +++ b/src/features/instantiate/containers/PartySelection.tsx @@ -17,10 +17,10 @@ import classes from 'src/features/instantiate/containers/PartySelection.module.c import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; import { - useCurrentParty, usePartiesAllowedToInstantiate, - useSetCurrentParty, + useSelectedParty, useSetHasSelectedParty, + useSetSelectedParty, } from 'src/features/party/PartiesProvider'; import { useNavigate } from 'src/features/routing/AppRoutingContext'; import { AltinnPalette } from 'src/theme/altinnAppTheme'; @@ -36,8 +36,8 @@ export const PartySelection = () => { const match = useMatch(`/party-selection/:errorCode`); const errorCode = match?.params.errorCode; - const selectParty = useSetCurrentParty(); - const selectedParty = useCurrentParty(); + const selectParty = useSetSelectedParty(); + const selectedParty = useSelectedParty(); const setUserHasSelectedParty = useSetHasSelectedParty(); const partiesAllowedToInstantiate = usePartiesAllowedToInstantiate() ?? []; diff --git a/src/features/instantiate/selection/ActiveInstancesProvider.tsx b/src/features/instantiate/selection/ActiveInstancesProvider.tsx index 19d1b24c16..01d0f28bc7 100644 --- a/src/features/instantiate/selection/ActiveInstancesProvider.tsx +++ b/src/features/instantiate/selection/ActiveInstancesProvider.tsx @@ -7,16 +7,16 @@ import { useAppQueries } from 'src/core/contexts/AppQueriesProvider'; import { delayedContext } from 'src/core/contexts/delayedContext'; import { createQueryContext } from 'src/core/contexts/queryContext'; import { InstantiateContainer } from 'src/features/instantiate/containers/InstantiateContainer'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useSelectedParty } from 'src/features/party/PartiesProvider'; const useActiveInstancesQuery = () => { const { fetchActiveInstances } = useAppQueries(); - const currentParty = useCurrentParty(); + const selectedParty = useSelectedParty(); const utils = useQuery({ - queryKey: ['getActiveInstances', currentParty?.partyId], + queryKey: ['getActiveInstances', selectedParty?.partyId], queryFn: async () => { - const simpleInstances = await fetchActiveInstances(currentParty?.partyId ?? -1); + const simpleInstances = await fetchActiveInstances(selectedParty?.partyId ?? -1); // Sort array by last changed date simpleInstances.sort((a, b) => new Date(a.lastChanged).getTime() - new Date(b.lastChanged).getTime()); diff --git a/src/features/instantiate/selection/InstanceSelection.tsx b/src/features/instantiate/selection/InstanceSelection.tsx index 8a71cd891e..9e171490e2 100644 --- a/src/features/instantiate/selection/InstanceSelection.tsx +++ b/src/features/instantiate/selection/InstanceSelection.tsx @@ -21,7 +21,7 @@ import { import classes from 'src/features/instantiate/selection/InstanceSelection.module.css'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useSelectedParty } from 'src/features/party/PartiesProvider'; import { useSetNavigationEffect } from 'src/features/routing/AppRoutingContext'; import { useIsMobileOrTablet } from 'src/hooks/useDeviceWidths'; import { focusMainContent } from 'src/hooks/useNavigatePage'; @@ -65,7 +65,7 @@ function InstanceSelection() { const mobileView = useIsMobileOrTablet(); const rowsPerPageOptions = instanceSelectionOptions?.rowsPerPageOptions ?? [10, 25, 50]; const instantiate = useInstantiation().instantiate; - const currentParty = useCurrentParty(); + const selectedParty = useSelectedParty(); const storeCallback = useSetNavigationEffect(); const { performProcess, isAnyProcessing, isThisProcessing: isLoading } = useIsProcessing(); @@ -275,9 +275,9 @@ function InstanceSelection() { size='md' onClick={() => performProcess(async () => { - if (currentParty) { + if (selectedParty) { storeCallback(focusMainContent); - await instantiate(currentParty.partyId); + await instantiate(selectedParty.partyId); } }) } diff --git a/src/features/party/PartiesProvider.tsx b/src/features/party/PartiesProvider.tsx index 7366502351..a023602c20 100644 --- a/src/features/party/PartiesProvider.tsx +++ b/src/features/party/PartiesProvider.tsx @@ -48,17 +48,17 @@ const usePartiesAllowedToInstantiateQuery = () => { }; // Also used for prefetching @see appPrefetcher.ts, partyPrefetcher.ts -export function useCurrentPartyQueryDef(enabled: boolean) { - const { fetchCurrentParty } = useAppQueries(); +export function useSelectedPartyQueryDef(enabled: boolean) { + const { fetchSelectedParty } = useAppQueries(); return { - queryKey: ['fetchUseCurrentParty', enabled], - queryFn: fetchCurrentParty, + queryKey: ['fetchUseSelectedParty', enabled], + queryFn: fetchSelectedParty, enabled, }; } -const useCurrentPartyQuery = (enabled: boolean) => { - const query = useQuery(useCurrentPartyQueryDef(enabled)); +const useSelectedPartyQuery = (enabled: boolean) => { + const query = useQuery(useSelectedPartyQueryDef(enabled)); useEffect(() => { query.error && window.logError('Fetching current party failed:\n', query.error); @@ -67,11 +67,11 @@ const useCurrentPartyQuery = (enabled: boolean) => { return query; }; -const useSetCurrentPartyMutation = () => { - const { doSetCurrentParty } = useAppMutations(); +const useSetSelectedPartyMutation = () => { + const { doSetSelectedParty } = useAppMutations(); return useMutation({ - mutationKey: ['doSetCurrentParty'], - mutationFn: (party: IParty) => doSetCurrentParty(party.partyId), + mutationKey: ['doSetSelectedParty'], + mutationFn: (party: IParty) => doSetSelectedParty(party.partyId), onError: (error: HttpClientError) => { window.logError('Setting current party failed:\n', error); }, @@ -87,26 +87,26 @@ const { Provider: PartiesProvider, useCtx: usePartiesAllowedToInstantiateCtx } = }), ); -interface CurrentParty { +interface SelectedParty { party: IParty | undefined; - currentIsValid: boolean | undefined; + selectedIsValid: boolean | undefined; userHasSelectedParty: boolean | undefined; setUserHasSelectedParty: (hasSelected: boolean) => void; setParty: (party: IParty) => Promise; } -const { Provider: RealCurrentPartyProvider, useCtx: useCurrentPartyCtx } = createContext({ - name: 'CurrentParty', +const { Provider: RealCSelectedPartyProvider, useCtx: useSelectedPartyCtx } = createContext({ + name: 'SelectedParty', required: false, default: { party: undefined, - currentIsValid: undefined, + selectedIsValid: undefined, userHasSelectedParty: undefined, setUserHasSelectedParty: () => { - throw new Error('CurrentPartyProvider not initialized'); + throw new Error('SelectedPartyProvider not initialized'); }, setParty: () => { - throw new Error('CurrentPartyProvider not initialized'); + throw new Error('SelectedPartyProvider not initialized'); }, }, }); @@ -116,11 +116,11 @@ const { Provider: RealCurrentPartyProvider, useCtx: useCurrentPartyCtx } = creat * That is, the selected party should only be used to determine the party that is used to instantiate an app or to select from previously instantiated apps. * When the user is filling out an app, the current party is always the user's party, found in the profile, filling out the form on behalf of the instance owner. */ -const CurrentPartyProvider = ({ children }: PropsWithChildren) => { +const SelectedPartyProvider = ({ children }: PropsWithChildren) => { const validParties = useValidParties(); const [sentToMutation, setSentToMutation] = useState(undefined); - const { mutateAsync, data: dataFromMutation, error: errorFromMutation } = useSetCurrentPartyMutation(); - const { data: partyFromQuery, isLoading, error: errorFromQuery } = useCurrentPartyQuery(true); + const { mutateAsync, data: dataFromMutation, error: errorFromMutation } = useSetSelectedPartyMutation(); + const { data: partyFromQuery, isLoading, error: errorFromQuery } = useSelectedPartyQuery(true); const [userHasSelectedParty, setUserHasSelectedParty] = useState(false); if (isLoading) { @@ -137,14 +137,14 @@ const CurrentPartyProvider = ({ children }: PropsWithChildren) => { } const partyFromMutation = dataFromMutation === 'Party successfully updated' ? sentToMutation : undefined; - const currentParty = partyFromMutation ?? partyFromQuery; - const currentIsValid = currentParty && validParties?.some((party) => party.partyId === currentParty.partyId); + const selectedParty = partyFromMutation ?? partyFromQuery; + const selectedIsValid = selectedParty && validParties?.some((party) => party.partyId === selectedParty.partyId); return ( - setUserHasSelectedParty(hasSelected), setParty: async (party) => { @@ -162,7 +162,7 @@ const CurrentPartyProvider = ({ children }: PropsWithChildren) => { }} > {children} - + ); }; @@ -175,7 +175,7 @@ export function PartyProvider({ children }: PropsWithChildren) { return ( - {children} + {children} ); } @@ -187,15 +187,15 @@ export const usePartiesAllowedToInstantiate = () => usePartiesAllowedToInstantia * Please note that the current party might not be allowed to instantiate, so you should * check the `canInstantiate` property as well. */ -export const useCurrentParty = () => useCurrentPartyCtx().party; -export const useCurrentPartyIsValid = () => useCurrentPartyCtx().currentIsValid; -export const useSetCurrentParty = () => useCurrentPartyCtx().setParty; +export const useSelectedParty = () => useSelectedPartyCtx().party; +export const useSelectedPartyIsValid = () => useSelectedPartyCtx().selectedIsValid; +export const useSetSelectedParty = () => useSelectedPartyCtx().setParty; export const useValidParties = () => flattenParties(usePartiesAllowedToInstantiateCtx() ?? []); -export const useHasSelectedParty = () => useCurrentPartyCtx().userHasSelectedParty; +export const useHasSelectedParty = () => useSelectedPartyCtx().userHasSelectedParty; -export const useSetHasSelectedParty = () => useCurrentPartyCtx().setUserHasSelectedParty; +export const useSetHasSelectedParty = () => useSelectedPartyCtx().setUserHasSelectedParty; export function useInstanceOwnerParty(): IParty | null { const parties = usePartiesAllowedToInstantiate() ?? []; diff --git a/src/layout/InstantiationButton/InstantiationButton.tsx b/src/layout/InstantiationButton/InstantiationButton.tsx index 59c731b02d..bd0124a1f3 100644 --- a/src/layout/InstantiationButton/InstantiationButton.tsx +++ b/src/layout/InstantiationButton/InstantiationButton.tsx @@ -5,7 +5,7 @@ import { useIsProcessing } from 'src/core/contexts/processingContext'; import { DataModels } from 'src/features/datamodel/DataModelsProvider'; import { FD } from 'src/features/formData/FormDataWrite'; import { useInstantiation } from 'src/features/instantiate/InstantiationContext'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useSelectedParty } from 'src/features/party/PartiesProvider'; import type { IInstantiationButtonComponentProvidedProps } from 'src/layout/InstantiationButton/InstantiationButtonComponent'; type Props = Omit, 'text'>; @@ -15,7 +15,7 @@ export const InstantiationButton = ({ children, ...props }: Props) => { const { instantiateWithPrefill, error } = useInstantiation(); const { performProcess, isAnyProcessing, isThisProcessing: isLoading } = useIsProcessing(); const prefill = FD.useMapping(props.mapping, DataModels.useDefaultDataType()); - const party = useCurrentParty(); + const party = useSelectedParty(); // const onClick = () => { // instantiateWithPrefill(props.node, { diff --git a/src/layout/SigningActions/PanelAwaitingCurrentUserSignature.tsx b/src/layout/SigningActions/PanelAwaitingCurrentUserSignature.tsx index e04e06469c..71c49461a6 100644 --- a/src/layout/SigningActions/PanelAwaitingCurrentUserSignature.tsx +++ b/src/layout/SigningActions/PanelAwaitingCurrentUserSignature.tsx @@ -11,7 +11,7 @@ import { UnknownError } from 'src/features/instantiate/containers/UnknownError'; import { Lang } from 'src/features/language/Lang'; import { useCurrentLanguage } from 'src/features/language/LanguageProvider'; import { useLanguage } from 'src/features/language/useLanguage'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useProfile } from 'src/features/profile/ProfileProvider'; import { signingQueries } from 'src/layout/SigneeList/api'; import { useAuthorizedOrganizationDetails, useUserSigneeParties } from 'src/layout/SigningActions/api'; import { OnBehalfOfChooser } from 'src/layout/SigningActions/OnBehalfOfChooser'; @@ -39,7 +39,7 @@ export function AwaitingCurrentUserSignaturePanel({ const canWrite = isAuthorised('write'); const selectedLanguage = useCurrentLanguage(); - const currentUserPartyId = useCurrentParty()?.partyId; + const currentUserPartyId = useProfile()?.partyId; const queryClient = useQueryClient(); const textResourceBindings = useNodeItem(node, (i) => i.textResourceBindings); const { langAsString } = useLanguage(); diff --git a/src/layout/SigningActions/PanelNoActionRequired.tsx b/src/layout/SigningActions/PanelNoActionRequired.tsx index 67af979ba2..810e22ebe2 100644 --- a/src/layout/SigningActions/PanelNoActionRequired.tsx +++ b/src/layout/SigningActions/PanelNoActionRequired.tsx @@ -4,7 +4,7 @@ import { Link } from '@digdir/designsystemet-react'; import { Button } from 'src/app-components/Button/Button'; import { Lang } from 'src/features/language/Lang'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useProfile } from 'src/features/profile/ProfileProvider'; import { SigningPanel } from 'src/layout/SigningActions/PanelSigning'; import classes from 'src/layout/SigningActions/SigningActions.module.css'; import { useNodeItem } from 'src/utils/layout/useNodeItem'; @@ -17,7 +17,7 @@ type NoActionRequiredPanelProps = { }; export function NoActionRequiredPanel({ node, hasSigned }: NoActionRequiredPanelProps) { - const currentUserPartyId = useCurrentParty()?.partyId; + const currentUserPartyId = useProfile()?.partyId; const textResourceBindings = useNodeItem(node, (i) => i.textResourceBindings); const titleHasSigned = diff --git a/src/layout/SigningActions/SigningActionsComponent.test.tsx b/src/layout/SigningActions/SigningActionsComponent.test.tsx index 1253893aed..87369c5ba7 100644 --- a/src/layout/SigningActions/SigningActionsComponent.test.tsx +++ b/src/layout/SigningActions/SigningActionsComponent.test.tsx @@ -9,7 +9,7 @@ import { randomUUID } from 'crypto'; import { useIsAuthorised } from 'src/features/instance/ProcessContext'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useProfile } from 'src/features/profile/ProfileProvider'; import { NotificationStatus, SigneeState, useSigneeList } from 'src/layout/SigneeList/api'; import { useSignaturesValidation, useUserSigneeParties } from 'src/layout/SigningActions/api'; import { AwaitingCurrentUserSignaturePanel } from 'src/layout/SigningActions/PanelAwaitingCurrentUserSignature'; @@ -25,7 +25,7 @@ jest.mock('src/utils/layout/useNodeItem'); jest.mock('react-router-dom'); jest.mock('src/features/instance/useProcessNext.tsx'); jest.mock('src/core/contexts/AppQueriesProvider'); -jest.mock('src/features/party/PartiesProvider'); +jest.mock('src/features/profile/ProfileProvider'); jest.mock('src/features/language/useLanguage'); jest.mock('src/features/language/Lang'); jest.mock('src/features/instance/ProcessContext'); @@ -88,7 +88,7 @@ describe('SigningActionsComponent', () => { } as unknown as ReturnType); jest.mocked(Lang).mockImplementation(({ id }: { id: string }) => id); - jest.mocked(useCurrentParty).mockReturnValue({ partyId: 123 } as unknown as ReturnType); + jest.mocked(useProfile).mockReturnValue({ partyId: 123 } as unknown as ReturnType); mockedUseSigneeList.mockReturnValue({ data: [], diff --git a/src/layout/SigningActions/SigningActionsComponent.tsx b/src/layout/SigningActions/SigningActionsComponent.tsx index 79350cdab9..9610e670e0 100644 --- a/src/layout/SigningActions/SigningActionsComponent.tsx +++ b/src/layout/SigningActions/SigningActionsComponent.tsx @@ -7,7 +7,7 @@ import { Panel } from 'src/app-components/Panel/Panel'; import { useIsAuthorised } from 'src/features/instance/ProcessContext'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useProfile } from 'src/features/profile/ProfileProvider'; import { useSigneeList } from 'src/layout/SigneeList/api'; import { useSignaturesValidation, useUserSigneeParties } from 'src/layout/SigningActions/api'; import { AwaitingCurrentUserSignaturePanel } from 'src/layout/SigningActions/PanelAwaitingCurrentUserSignature'; @@ -27,7 +27,7 @@ export function SigningActionsComponent({ node }: PropsFromGenericComponent<'Sig error: signeeListError, } = useSigneeList(instanceOwnerPartyId, instanceGuid, taskId); - const currentUserPartyId = useCurrentParty()?.partyId; + const currentUserPartyId = useProfile()?.partyId; const { langAsString } = useLanguage(); const isAuthorised = useIsAuthorised(); diff --git a/src/layout/SigningActions/api.ts b/src/layout/SigningActions/api.ts index 81fa9bd6fb..7d40cc8c6b 100644 --- a/src/layout/SigningActions/api.ts +++ b/src/layout/SigningActions/api.ts @@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { z } from 'zod'; -import { useCurrentParty } from 'src/features/party/PartiesProvider'; +import { useProfile } from 'src/features/profile/ProfileProvider'; import { useBackendValidationQuery } from 'src/features/validation/backendValidation/backendValidationQuery'; import { useSigneeList } from 'src/layout/SigneeList/api'; import { httpGet } from 'src/utils/network/sharedNetworking'; @@ -58,7 +58,7 @@ export function useUserSigneeParties() { instanceGuid!, ); - const currentUserPartyId = useCurrentParty()?.partyId; + const currentUserPartyId = useProfile()?.partyId; if (!signeeList || !currentUserPartyId) { return []; diff --git a/src/layout/SigningActions/utils.test.ts b/src/layout/SigningActions/utils.test.ts index df9602e3db..df37b449ec 100644 --- a/src/layout/SigningActions/utils.test.ts +++ b/src/layout/SigningActions/utils.test.ts @@ -13,8 +13,8 @@ describe('getCurrentUserStatus', () => { partyId: 123, }; - const unsignedSignee = { - name: 'Jane Smith', + const unsignedOrgSignee = { + name: null, organization: 'Organization B', signedTime: null, hasSigned: false, @@ -23,9 +23,19 @@ describe('getCurrentUserStatus', () => { partyId: 456, }; + const signedOrgSignee = { + name: 'Jane Smith', + organization: 'Organization C', + signedTime: new Date().toISOString(), + hasSigned: true, + delegationSuccessful: true, + notificationStatus: NotificationStatus.Sent, + partyId: 457, + }; + const currentUserSignee = { name: 'Current User', - organization: 'Organization C', + organization: null, signedTime: null, hasSigned: false, delegationSuccessful: true, @@ -36,7 +46,7 @@ describe('getCurrentUserStatus', () => { describe('getCurrentUserStatus', () => { it('should return "notSigning" when user does not have permission to sign', () => { const currentUserPartyId = 789; - const userSignees = [currentUserSignee, unsignedSignee]; + const userSignees = [currentUserSignee, unsignedOrgSignee]; const canSign = false; const result = getCurrentUserStatus(currentUserPartyId, userSignees, canSign); @@ -46,7 +56,7 @@ describe('getCurrentUserStatus', () => { it('should return "awaitingSignature" when the current user is not in the signee list, but has permission to sign', () => { const currentUserPartyId = 999; // Not in the list - const userSignees = [unsignedSignee, signedSignee]; + const userSignees = [unsignedOrgSignee, signedSignee]; const canSign = true; const result = getCurrentUserStatus(currentUserPartyId, userSignees, canSign); @@ -66,7 +76,7 @@ describe('getCurrentUserStatus', () => { it('should return "awaitingSignature" if any signee has not signed', () => { const currentUserPartyId = 789; - const userSignees = [{ ...currentUserSignee, partyId: 789 }, unsignedSignee, signedSignee]; + const userSignees = [{ ...currentUserSignee, partyId: 789 }, unsignedOrgSignee, signedSignee]; const canSign = true; const result = getCurrentUserStatus(currentUserPartyId, userSignees, canSign); @@ -74,23 +84,22 @@ describe('getCurrentUserStatus', () => { expect(result).toBe('awaitingSignature'); }); - it('should return "signed" if all signees have signed', () => { - const currentUserPartyId = 789; + it('should return "signed" if all signees have signed and the current user (person) is in the signed list', () => { const userSignees = [ { ...currentUserSignee, hasSigned: true, signedTime: new Date().toISOString() }, - { ...unsignedSignee, hasSigned: true, signedTime: new Date().toISOString() }, + { ...unsignedOrgSignee, hasSigned: true, signedTime: new Date().toISOString() }, signedSignee, ]; const canSign = true; - const result = getCurrentUserStatus(currentUserPartyId, userSignees, canSign); + const result = getCurrentUserStatus(currentUserSignee.partyId, userSignees, canSign); expect(result).toBe('signed'); }); it('should handle undefined currentUserPartyId correctly', () => { const currentUserPartyId = undefined; - const userSignees = [unsignedSignee, signedSignee]; + const userSignees = [unsignedOrgSignee, signedSignee]; const canSign = true; const result = getCurrentUserStatus(currentUserPartyId, userSignees, canSign); @@ -101,7 +110,7 @@ describe('getCurrentUserStatus', () => { it('should handle a mix of signed and unsigned signees correctly', () => { const currentUserPartyId = 789; - const userSignees = [{ ...currentUserSignee, partyId: 789 }, signedSignee, unsignedSignee]; + const userSignees = [{ ...currentUserSignee, partyId: 789 }, signedSignee, unsignedOrgSignee]; const canSign = true; const result = getCurrentUserStatus(currentUserPartyId, userSignees, canSign); @@ -109,6 +118,19 @@ describe('getCurrentUserStatus', () => { // Since there's at least one unsigned signee, it should return 'awaitingSignature' expect(result).toBe('awaitingSignature'); }); + + it('should return "awaitingSignature" if the current user is not in the list, but has signed for other orgs', () => { + const currentUserPartyId = 789; // Not in the list + const userSignees = [ + { organization: 'Org 1', partyId: 'org1', hasSigned: true }, + { organization: 'Org 2', partyId: 'org2', hasSigned: true }, + ] as unknown as SigneeState[]; + const canSign = true; + + const result = getCurrentUserStatus(currentUserPartyId, userSignees, canSign); + + expect(result).toBe('awaitingSignature'); + }); }); describe('comprehensive test matrix', () => { @@ -122,14 +144,14 @@ describe('getCurrentUserStatus', () => { { description: 'User cannot sign', currentUserPartyId: 123, - userSignees: [unsignedSignee], + userSignees: [unsignedOrgSignee], canSign: false, expected: 'notSigning', }, { description: 'User not in list but can sign', currentUserPartyId: 999, - userSignees: [unsignedSignee], + userSignees: [unsignedOrgSignee], canSign: true, expected: 'awaitingSignature', }, @@ -143,14 +165,21 @@ describe('getCurrentUserStatus', () => { { description: 'Has unsigned signees', currentUserPartyId: 123, - userSignees: [signedSignee, unsignedSignee], + userSignees: [signedSignee, unsignedOrgSignee], canSign: true, expected: 'awaitingSignature', }, { - description: 'All signees have signed', + description: 'All signees signed and current user person is not in the list', currentUserPartyId: 123, - userSignees: [signedSignee, { ...unsignedSignee, hasSigned: true, signedTime: new Date().toISOString() }], + userSignees: [signedSignee, signedOrgSignee], + canSign: true, + expected: 'awaitingSignature', + }, + { + description: 'All signees have signed including current user as person', + currentUserPartyId: currentUserSignee.partyId, + userSignees: [{ ...currentUserSignee, hasSigned: true }, signedSignee, signedOrgSignee], canSign: true, expected: 'signed', }, diff --git a/src/layout/SigningActions/utils.ts b/src/layout/SigningActions/utils.ts index 1e99a10d6f..6b7f3ee8c7 100644 --- a/src/layout/SigningActions/utils.ts +++ b/src/layout/SigningActions/utils.ts @@ -18,11 +18,17 @@ export function getCurrentUserStatus( return 'notSigning'; } - // If the current user is not listed as a signee, but they have sign permission, they should still be able to sign - const currentUserIsInList = userSignees.some((signee) => signee.partyId === currentUserPartyId); const hasUnsignedSignees = userSignees.some((signee) => !signee.hasSigned); + if (hasUnsignedSignees) { + return 'awaitingSignature'; + } + + // If the current user is not listed as a person signee, but they have sign permission, they should still be able to sign + const currentUserIsInList = userSignees.some( + (signee) => signee.partyId === currentUserPartyId && !signee.organization, + ); - if (!currentUserIsInList || hasUnsignedSignees) { + if (!currentUserIsInList) { return 'awaitingSignature'; } diff --git a/src/queries/appPrefetcher.ts b/src/queries/appPrefetcher.ts index 5a69dad5b5..f4b5f7144f 100644 --- a/src/queries/appPrefetcher.ts +++ b/src/queries/appPrefetcher.ts @@ -7,7 +7,7 @@ import { useLayoutSetsQueryDef } from 'src/features/form/layoutSets/LayoutSetsPr import { useInstanceDataQueryDef } from 'src/features/instance/InstanceContext'; import { getProcessQueryDef } from 'src/features/instance/ProcessContext'; import { useOrgsQueryDef } from 'src/features/orgs/OrgsProvider'; -import { useCurrentPartyQueryDef, usePartiesQueryDef } from 'src/features/party/PartiesProvider'; +import { usePartiesQueryDef, useSelectedPartyQueryDef } from 'src/features/party/PartiesProvider'; import { useProfileQueryDef } from 'src/features/profile/ProfileProvider'; /** @@ -26,7 +26,7 @@ export function AppPrefetcher() { usePrefetchQuery(useOrgsQueryDef()); usePrefetchQuery(useApplicationSettingsQueryDef()); usePrefetchQuery(usePartiesQueryDef(true), Boolean(instanceOwnerPartyId)); - usePrefetchQuery(useCurrentPartyQueryDef(true), Boolean(instanceOwnerPartyId)); + usePrefetchQuery(useSelectedPartyQueryDef(true), Boolean(instanceOwnerPartyId)); usePrefetchQuery(useInstanceDataQueryDef(false, instanceOwnerPartyId, instanceGuid)); usePrefetchQuery(getProcessQueryDef(instanceId)); diff --git a/src/queries/partyPrefetcher.ts b/src/queries/partyPrefetcher.ts index 6d22acd46d..cd2dd9c8c3 100644 --- a/src/queries/partyPrefetcher.ts +++ b/src/queries/partyPrefetcher.ts @@ -1,5 +1,5 @@ import { usePrefetchQuery } from 'src/core/queries/usePrefetchQuery'; -import { useCurrentPartyQueryDef, usePartiesQueryDef } from 'src/features/party/PartiesProvider'; +import { usePartiesQueryDef, useSelectedPartyQueryDef } from 'src/features/party/PartiesProvider'; import { useShouldFetchProfile } from 'src/features/profile/ProfileProvider'; /** @@ -9,7 +9,7 @@ export function PartyPrefetcher() { const enabled = useShouldFetchProfile(); usePrefetchQuery(usePartiesQueryDef(true), enabled); - usePrefetchQuery(useCurrentPartyQueryDef(true), enabled); + usePrefetchQuery(useSelectedPartyQueryDef(true), enabled); return null; } diff --git a/src/queries/queries.ts b/src/queries/queries.ts index 24d8cdbb21..a799e61091 100644 --- a/src/queries/queries.ts +++ b/src/queries/queries.ts @@ -14,7 +14,6 @@ import { applicationMetadataApiUrl, applicationSettingsApiUrl, appPath, - currentPartyUrl, getActionsUrl, getActiveInstancesUrl, getCreateInstancesUrl, @@ -40,11 +39,12 @@ import { getProcessStateUrl, getRedirectUrl, getRulehandlerUrl, - getSetCurrentPartyUrl, + getSetSelectedPartyUrl, getValidationUrl, instancesControllerUrl, profileApiUrl, refreshJwtTokenUrl, + selectedPartyUrl, textResourcesUrl, validPartiesUrl, } from 'src/utils/urls/appUrlHelper'; @@ -81,8 +81,8 @@ import type { IProfile, } from 'src/types/shared'; -export const doSetCurrentParty = (partyId: number | string) => - putWithoutConfig<'Party successfully updated' | string | null>(getSetCurrentPartyUrl(partyId)); +export const doSetSelectedParty = (partyId: number | string) => + putWithoutConfig<'Party successfully updated' | string | null>(getSetSelectedPartyUrl(partyId)); export const doInstantiateWithPrefill = async (data: Instantiation, language?: string): Promise => cleanUpInstanceData((await httpPost(getInstantiateUrl(language), undefined, data)).data); @@ -231,7 +231,7 @@ export const fetchApplicationMetadata = () => httpGet => httpGet(applicationSettingsApiUrl); -export const fetchCurrentParty = (): Promise => httpGet(currentPartyUrl); +export const fetchSelectedParty = (): Promise => httpGet(selectedPartyUrl); export const fetchFooterLayout = (): Promise => httpGet(getFooterLayoutUrl()); diff --git a/src/test/renderWithProviders.tsx b/src/test/renderWithProviders.tsx index 22895d33dd..f19dd3025e 100644 --- a/src/test/renderWithProviders.tsx +++ b/src/test/renderWithProviders.tsx @@ -123,7 +123,7 @@ export const makeMutationMocks = any>( doPatchFormData: makeMock('doPatchFormData'), doPatchMultipleFormData: makeMock('doPatchMultipleFormData'), doPostStatelessFormData: makeMock('doPostStatelessFormData'), - doSetCurrentParty: makeMock('doSetCurrentParty'), + doSetSelectedParty: makeMock('doSetSelectedParty'), doInstantiate: makeMock('doInstantiate'), doProcessNext: makeMock('doProcessNext'), doInstantiateWithPrefill: makeMock('doInstantiateWithPrefill'), @@ -135,7 +135,7 @@ export const makeMutationMocks = any>( const defaultQueryMocks: AppQueries = { fetchLogo: async () => getLogoMock(), fetchActiveInstances: async () => [], - fetchCurrentParty: async () => getPartyMock(), + fetchSelectedParty: async () => getPartyMock(), fetchApplicationSettings: async () => ({}), fetchFooterLayout: async () => ({ footer: [] }) as IFooterLayout, fetchLayoutSets: async () => getLayoutSetsMock(), diff --git a/src/utils/urls/appUrlHelper.test.ts b/src/utils/urls/appUrlHelper.test.ts index 37f8bc8098..e829b2de6f 100644 --- a/src/utils/urls/appUrlHelper.test.ts +++ b/src/utils/urls/appUrlHelper.test.ts @@ -12,7 +12,7 @@ import { getProcessStateUrl, getRedirectUrl, getRulehandlerUrl, - getSetCurrentPartyUrl, + getSetSelectedPartyUrl, getUpgradeAuthLevelUrl, getValidationUrl, redirectToUpgrade, @@ -27,8 +27,8 @@ describe('Frontend urlHelper.ts', () => { 'https://local.altinn.cloud/ttd/test/api/v1/parties?allowedtoinstantiatefilter=true', ); }); - it('should return the expected url for getSetCurrentPartyUrl', () => { - expect(getSetCurrentPartyUrl(12345)).toBe('https://local.altinn.cloud/ttd/test/api/v1/parties/12345'); + it('should return the expected url for getSetSelectedPartyUrl', () => { + expect(getSetSelectedPartyUrl(12345)).toBe('https://local.altinn.cloud/ttd/test/api/v1/parties/12345'); }); it('should return the expected url for textResourcesUrl', () => { expect(textResourcesUrl('nb')).toBe('https://local.altinn.cloud/ttd/test/api/v1/texts/nb'); diff --git a/src/utils/urls/appUrlHelper.ts b/src/utils/urls/appUrlHelper.ts index d11b2b6c25..6cef768e58 100644 --- a/src/utils/urls/appUrlHelper.ts +++ b/src/utils/urls/appUrlHelper.ts @@ -11,7 +11,7 @@ export const applicationMetadataApiUrl = `${appPath}/api/v1/applicationmetadata` export const applicationSettingsApiUrl = `${appPath}/api/v1/applicationsettings`; export const invalidateCookieUrl = `${appPath}/api/authentication/invalidatecookie`; export const validPartiesUrl = `${appPath}/api/v1/parties?allowedtoinstantiatefilter=true`; -export const currentPartyUrl = `${appPath}/api/authorization/parties/current?returnPartyObject=true`; +export const selectedPartyUrl = `${appPath}/api/authorization/parties/current?returnPartyObject=true`; export const instancesControllerUrl = `${appPath}/instances`; export const refreshJwtTokenUrl = `${appPath}/api/authentication/keepAlive`; export const applicationLanguagesUrl = `${appPath}/api/v1/applicationlanguages`; @@ -21,7 +21,7 @@ export const getInstantiateUrl = (language?: string) => { return `${appPath}/instances/create${queryString}`; }; -export const getSetCurrentPartyUrl = (partyId: string | number) => `${appPath}/api/v1/parties/${partyId}`; +export const getSetSelectedPartyUrl = (partyId: string | number) => `${appPath}/api/v1/parties/${partyId}`; export const textResourcesUrl = (language: string) => `${origin}/${org}/${app}/api/v1/texts/${language}`; diff --git a/test/e2e/integration/frontend-test/party-selection.ts b/test/e2e/integration/frontend-test/party-selection.ts index a06c98568f..f8660a47fc 100644 --- a/test/e2e/integration/frontend-test/party-selection.ts +++ b/test/e2e/integration/frontend-test/party-selection.ts @@ -33,7 +33,7 @@ describe('Party selection', () => { it('Should skip party selection if you can only represent one person', () => { cyMockResponses({ preSelectedParty: CyPartyMocks.ExamplePerson1.partyId, - currentParty: CyPartyMocks.ExamplePerson1, + selectedParty: CyPartyMocks.ExamplePerson1, allowedToInstantiate: [CyPartyMocks.ExamplePerson1], }); cy.intercept( diff --git a/test/e2e/pageobjects/party-mocks.ts b/test/e2e/pageobjects/party-mocks.ts index f4146ca53b..6272236270 100644 --- a/test/e2e/pageobjects/party-mocks.ts +++ b/test/e2e/pageobjects/party-mocks.ts @@ -95,7 +95,7 @@ export const CyPartyMocks = { interface Mockable { preSelectedParty?: number; - currentParty?: IParty; + selectedParty?: IParty; allowedToInstantiate?: IParty[] | ((parties: IParty[]) => IParty[]); doNotPromptForParty?: boolean; appPromptForPartyOverride?: IncomingApplicationMetadata['promptForParty']; @@ -109,10 +109,10 @@ export function cyMockResponses(whatToMock: Mockable) { cy.setCookie('AltinnPartyId', whatToMock.preSelectedParty.toString()); } - if (whatToMock.currentParty) { + if (whatToMock.selectedParty) { cy.intercept('GET', `**/api/authorization/parties/current?returnPartyObject=true`, (req) => { req.on('response', (res) => { - res.body = whatToMock.currentParty; + res.body = whatToMock.selectedParty; }); }); }