diff --git a/src/pages/ConsumerClientManagePage/ConsumerClientManage.page.tsx b/src/pages/ConsumerClientManagePage/ConsumerClientManage.page.tsx index f5fb5bbac..052a12360 100644 --- a/src/pages/ConsumerClientManagePage/ConsumerClientManage.page.tsx +++ b/src/pages/ConsumerClientManagePage/ConsumerClientManage.page.tsx @@ -7,7 +7,6 @@ import { TabContext, TabList, TabPanel } from '@mui/lab' import { Alert, Button, Grid, Link, Stack, Tab, Typography } from '@mui/material' import React from 'react' import { useTranslation } from 'react-i18next' -import { VoucherInstructions } from './components/VoucherInstructions' import { useClientKind } from '@/hooks/useClientKind' import { ClientOperators } from './components/ClientOperators' import { ClientPublicKeys } from './components/ClientPublicKeys' @@ -19,12 +18,16 @@ import SyncIcon from '@mui/icons-material/Sync' import { useDrawerState } from '@/hooks/useDrawerState' import { SetClientAdminDrawer } from './components/SetClientAdminDrawer/SetClientAdminDrawer' import { apiV2GuideLink } from '@/config/constants' +import { useNavigate } from '@/router' +import type { ActionItemButton } from '@/types/common.types' const ConsumerClientManagePage: React.FC = () => { const { t } = useTranslation('client', { keyPrefix: 'edit' }) + const { t: tCommon } = useTranslation('common', { keyPrefix: 'actions' }) const { clientId } = useParams<'SUBSCRIBE_CLIENT_EDIT' | 'SUBSCRIBE_INTEROP_M2M_CLIENT_EDIT'>() const clientKind = useClientKind() - const { activeTab, updateActiveTab } = useActiveTab('voucher') + const { activeTab, updateActiveTab } = useActiveTab('clientOperators') + const navigate = useNavigate() const { data: client, isLoading: isLoadingClient } = useQuery(ClientQueries.getSingle(clientId)) @@ -44,11 +47,18 @@ const ConsumerClientManagePage: React.FC = () => { }) } + const voucherSimulationAction: ActionItemButton = { + action: () => + navigate(clientKind === 'API' ? 'SIMULATE_GET_VOUCHER_API' : 'SIMULATE_GET_VOUCHER_CONSUMER'), + label: tCommon('simulateVoucher'), + variant: 'contained', + } + return ( { )} - - - - - diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsContext.tsx b/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsContext.tsx deleted file mode 100644 index dbe91bd11..000000000 --- a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsContext.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React from 'react' -import { createContext } from '@/utils/common.utils' -import noop from 'lodash/noop' -import { useSearchParams } from 'react-router-dom' - -type VoucherInstructionsContextType = { - selectedPurposeId: string | undefined - selectedKeyId: string | undefined - handleSelectedPurposeIdChange: (purpose: string) => void - handleSelectedKeyIdChange: (key: string) => void - clientId: string - goToNextStep: VoidFunction - goToPreviousStep: VoidFunction -} - -const initialState: VoucherInstructionsContextType = { - selectedPurposeId: undefined, - selectedKeyId: undefined, - handleSelectedPurposeIdChange: noop, - handleSelectedKeyIdChange: noop, - clientId: '', - goToNextStep: noop, - goToPreviousStep: noop, -} - -const { useContext, Provider } = createContext( - 'VoucherInstructionsContext', - initialState -) - -type VoucherInstructionsContextProviderProps = { - children: React.ReactNode - clientId: string - goToNextStep: VoidFunction - goToPreviousStep: VoidFunction -} - -const VoucherInstructionsContextProvider: React.FC = ({ - children, - clientId, - goToNextStep, - goToPreviousStep, -}) => { - const [searchParams, setSearchParams] = useSearchParams() - - const handleSelectedPurposeIdChange = React.useCallback( - (purposeId: string) => { - setSearchParams((prev) => { - prev.set('purposeId', purposeId) - return prev - }) - }, - [setSearchParams] - ) - - const handleSelectedKeyIdChange = React.useCallback( - (keyId: string) => { - setSearchParams((prev) => { - prev.set('keyId', keyId) - return prev - }) - }, - [setSearchParams] - ) - - const selectedPurposeId = searchParams.get('purposeId') || undefined - const selectedKeyId = searchParams.get('keyId') || undefined - - const providerValue = React.useMemo( - () => ({ - selectedPurposeId, - handleSelectedPurposeIdChange, - selectedKeyId, - handleSelectedKeyIdChange, - clientId, - goToNextStep, - goToPreviousStep, - }), - [ - selectedPurposeId, - handleSelectedPurposeIdChange, - selectedKeyId, - handleSelectedKeyIdChange, - clientId, - goToNextStep, - goToPreviousStep, - ] - ) - - return {children} -} - -export { useContext as useVoucherInstructionsContext, VoucherInstructionsContextProvider } diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1.tsx b/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1.tsx deleted file mode 100644 index 233b6e24b..000000000 --- a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import React from 'react' -import { SectionContainer } from '@/components/layout/containers' -import { useTranslation } from 'react-i18next' -import { Alert, FormControl, InputLabel, MenuItem, Select } from '@mui/material' -import { useVoucherInstructionsContext } from '../VoucherInstructionsContext' -import { ClientQueries } from '@/api/client' -import OpenInNewIcon from '@mui/icons-material/OpenInNew' -import ApiIcon from '@mui/icons-material/Api' -import ArrowForwardIcon from '@mui/icons-material/ArrowForward' -import { verifyVoucherGuideLink } from '@/config/constants' -import { VoucherInstructionsStep1CurrentIdsDrawer } from './VoucherInstructionsStep1CurrentIdsDrawer' -import { useDrawerState } from '@/hooks/useDrawerState' -import { StepActions } from '@/components/shared/StepActions' -import { useClientKind } from '@/hooks/useClientKind' -import { useSuspenseQuery } from '@tanstack/react-query' - -export const VoucherInstructionsStep1: React.FC = () => { - const { t } = useTranslation('voucher') - const clientKind = useClientKind() - const { - clientId, - selectedPurposeId, - handleSelectedPurposeIdChange, - handleSelectedKeyIdChange, - selectedKeyId, - goToNextStep, - } = useVoucherInstructionsContext() - - const { isOpen, openDrawer, closeDrawer } = useDrawerState() - - const { data: clientKeys } = useSuspenseQuery(ClientQueries.getAllKeysList({ clientId })) - const { data: client } = useSuspenseQuery(ClientQueries.getSingle(clientId)) - - const purposeSelectLabelId = React.useId() - const purposeSelectId = React.useId() - const keySelectLabelId = React.useId() - const keySelectId = React.useId() - - const purposes = client.purposes - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault() - goToNextStep() - } - - const canGoToNextStep = - clientKind === 'CONSUMER' ? Boolean(selectedKeyId && selectedPurposeId) : Boolean(selectedKeyId) - - if (clientKind === 'CONSUMER' && purposes.length === 0) { - return {t('noPurposesLabel')} - } - - if (clientKeys.length === 0) { - return {t('noKeysLabel')} - } - - return ( - <> -
- , - label: t('step1.goToTechnicalDocsLabel'), - href: verifyVoucherGuideLink, - target: '_blank', - }, - { - startIcon: , - label: t('step1.showCurrentSelectionIds'), - component: 'button', - type: 'button', - onClick: openDrawer, - }, - ]} - > - {clientKind === 'CONSUMER' && ( - - - {t('step1.purposeSelectInput.label')} - - - - )} - - - {t('step1.keySelectInput.label')} - - - - , - }} - /> - - - - ) -} diff --git a/src/pages/ConsumerSimulateGetVoucherPage/ConsumerSimulateGetVoucherPage.page.tsx b/src/pages/ConsumerSimulateGetVoucherPage/ConsumerSimulateGetVoucherPage.page.tsx new file mode 100644 index 000000000..4d50f3e1c --- /dev/null +++ b/src/pages/ConsumerSimulateGetVoucherPage/ConsumerSimulateGetVoucherPage.page.tsx @@ -0,0 +1,15 @@ +import { PageContainer } from '@/components/layout/containers' +import { useTranslation } from 'react-i18next' +import { VoucherInstructions } from './components/VoucherInstructions' + +const ConsumerDebugVoucherPage: React.FC = () => { + const { t } = useTranslation('pages', { keyPrefix: 'consumerSimulateGetVoucher' }) + + return ( + + + + ) +} + +export default ConsumerDebugVoucherPage diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/ClientVoucherIntructionsPurposeSelect.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/ClientVoucherIntructionsPurposeSelect.tsx similarity index 100% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/ClientVoucherIntructionsPurposeSelect.tsx rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/ClientVoucherIntructionsPurposeSelect.tsx diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/CodeSnippetPreview.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/CodeSnippetPreview.tsx similarity index 100% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/CodeSnippetPreview.tsx rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/CodeSnippetPreview.tsx diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructions.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructions.tsx similarity index 68% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructions.tsx rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructions.tsx index 7eb7ef087..198e0161a 100644 --- a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructions.tsx +++ b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructions.tsx @@ -1,7 +1,6 @@ import React from 'react' import { Stepper } from '@/components/shared/Stepper' import { useActiveStep } from '@/hooks/useActiveStep' -import { Grid } from '@mui/material' import { useTranslation } from 'react-i18next' import { useClientKind } from '@/hooks/useClientKind' import { SectionContainerSkeleton } from '@/components/layout/containers' @@ -11,13 +10,8 @@ import { VoucherInstructionsStep1 } from './VoucherInstructionsStep1' import { VoucherInstructionsStep2 } from './VoucherInstructionsStep2' import { VoucherInstructionsStep3 } from './VoucherInstructionsStep3' import { VoucherInstructionsStep4 } from './VoucherInstructionsStep4' -import { HeadSection } from '@/components/shared/HeadSection' -interface VoucherInstructionsProps { - clientId: string -} - -export const VoucherInstructions: React.FC = ({ clientId }) => { +export const VoucherInstructions: React.FC = () => { const { t } = useTranslation('voucher') const clientKind = useClientKind() const { activeStep, forward, back } = useActiveStep() @@ -38,23 +32,17 @@ export const VoucherInstructions: React.FC = ({ client const contextProps = { goToPreviousStep: back, goToNextStep: forward, - clientId, } return ( <> - - - - - } - > - - - - + + } + > + + ) diff --git a/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsContext.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsContext.tsx new file mode 100644 index 000000000..e1edac28c --- /dev/null +++ b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsContext.tsx @@ -0,0 +1,42 @@ +import React from 'react' +import { createContext } from '@/utils/common.utils' +import noop from 'lodash/noop' + +type VoucherInstructionsContextType = { + goToNextStep: VoidFunction + goToPreviousStep: VoidFunction +} + +const initialState: VoucherInstructionsContextType = { + goToNextStep: noop, + goToPreviousStep: noop, +} + +const { useContext, Provider } = createContext( + 'VoucherInstructionsContext', + initialState +) + +type VoucherInstructionsContextProviderProps = { + children: React.ReactNode + goToNextStep: VoidFunction + goToPreviousStep: VoidFunction +} + +const VoucherInstructionsContextProvider: React.FC = ({ + children, + goToNextStep, + goToPreviousStep, +}) => { + const providerValue = React.useMemo( + () => ({ + goToNextStep, + goToPreviousStep, + }), + [goToNextStep, goToPreviousStep] + ) + + return {children} +} + +export { useContext as useVoucherInstructionsContext, VoucherInstructionsContextProvider } diff --git a/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1.tsx new file mode 100644 index 000000000..74fcbb143 --- /dev/null +++ b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1.tsx @@ -0,0 +1,195 @@ +import React, { useEffect } from 'react' +import { SectionContainer } from '@/components/layout/containers' +import { useTranslation } from 'react-i18next' +import { Alert, Box, FormControl } from '@mui/material' +import { ClientQueries } from '@/api/client' +import OpenInNewIcon from '@mui/icons-material/OpenInNew' +import ApiIcon from '@mui/icons-material/Api' +import ArrowForwardIcon from '@mui/icons-material/ArrowForward' +import { verifyVoucherGuideLink } from '@/config/constants' +import { VoucherInstructionsStep1CurrentIdsDrawer } from './VoucherInstructionsStep1CurrentIdsDrawer' +import { useDrawerState } from '@/hooks/useDrawerState' +import { StepActions } from '@/components/shared/StepActions' +import { useClientKind } from '@/hooks/useClientKind' +import { useQuery } from '@tanstack/react-query' +import { useAutocompleteTextInput } from '@pagopa/interop-fe-commons' +import { useForm, FormProvider, type SubmitHandler } from 'react-hook-form' +import { RHFAutocompleteSingle, RHFSelect } from '@/components/shared/react-hook-form-inputs' +import { useVoucherInstructionsContext } from '../VoucherInstructionsContext' +import { useSearchParams } from 'react-router-dom' + +interface VoucherInstructionsStep1Form { + clientId: string | null + purposeId: string | null + keyId: string | null +} + +export const VoucherInstructionsStep1: React.FC = () => { + const { t } = useTranslation('voucher') + const clientKind = useClientKind() + const { goToNextStep } = useVoucherInstructionsContext() + const [searchParams, setSearchParams] = useSearchParams() + + const formMethods = useForm({ + defaultValues: { + clientId: searchParams.get('clientId'), + purposeId: searchParams.get('purposeId'), + keyId: searchParams.get('keyId'), + }, + }) + + const { watch, handleSubmit, setValue } = formMethods + + const clientId = watch('clientId') || '' + const purposeId = watch('purposeId') || '' + const keyId = watch('keyId') + + const [clientSearch, setClientSearch] = useAutocompleteTextInput('') + const { isOpen, openDrawer, closeDrawer } = useDrawerState() + + const { data: clients, isFetching: isFetchingClients } = useQuery({ + ...ClientQueries.getList({ + kind: clientKind, + q: clientSearch, + offset: 0, + limit: 50, + }), + }) + + const { data: clientKeys, isFetching: isFetchingKeys } = useQuery({ + ...ClientQueries.getAllKeysList({ clientId }), + enabled: Boolean(clientId), + }) + + const { data: client, isFetching: isFetchingClient } = useQuery({ + ...ClientQueries.getSingle(clientId), + enabled: Boolean(clientId), + }) + + const purposes = client?.purposes + + const canGoToNextStep = clientKind === 'CONSUMER' ? Boolean(keyId && purposeId) : Boolean(keyId) + + const options = React.useMemo(() => { + const results = clients?.results ?? [] + return results.map((att) => ({ + label: att.name, + value: att.id, + })) + }, [clients]) + + const onSubmit: SubmitHandler = (values) => { + if (clientKind === 'CONSUMER' && !Boolean(values.keyId && values.purposeId)) return + + if (clientKind === 'API' && !Boolean(values.keyId)) return + + setSearchParams((prev) => { + if (values.clientId) prev.set('clientId', values.clientId) + if (values.purposeId) prev.set('purposeId', values.purposeId) + if (values.keyId) prev.set('keyId', values.keyId) + return prev + }) + goToNextStep() + } + + /** + * Subscribes to the form values changes + * and updates the actual visible questions on values change. + */ + useEffect(() => { + const subscription = watch((_, { name }) => { + if (name === 'clientId') { + setValue('purposeId', null) + setValue('keyId', null) + setSearchParams({}) + } + }) + return () => subscription.unsubscribe() + }, [watch, setValue, setSearchParams]) + + return ( + + + , + label: t('step1.goToTechnicalDocsLabel'), + href: verifyVoucherGuideLink, + target: '_blank', + }, + { + startIcon: , + label: t('step1.showCurrentSelectionIds'), + component: 'button', + type: 'button', + onClick: openDrawer, + }, + ]} + > + + setClientSearch(value)} + options={options} + loading={isFetchingClients} + /> + + {clientKind === 'CONSUMER' ? ( + !clientId || purposes?.length || isFetchingClient ? ( + + ({ + label: `${purpose.title} per ${purpose.eservice.name}`, + value: purpose.purposeId, + }))} + rules={{ required: true }} + disabled={!clientId || isFetchingClient} + /> + + ) : ( + + {t('noPurposesLabel')} + + ) + ) : null} + {!Boolean(clientId) || clientKeys?.length || isFetchingClient || isFetchingKeys ? ( + + ({ label: key.name, value: key.keyId }))} + rules={{ required: true }} + disabled={!clientKeys || isFetchingClients || isFetchingKeys || isFetchingClient} + /> + + ) : ( + + {t('noKeysLabel')} + + )} + + , + }} + /> + + + + ) +} diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1CurrentIdsDrawer.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1CurrentIdsDrawer.tsx similarity index 92% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1CurrentIdsDrawer.tsx rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1CurrentIdsDrawer.tsx index b1d87c4c4..31cda6b1a 100644 --- a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1CurrentIdsDrawer.tsx +++ b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep1/VoucherInstructionsStep1CurrentIdsDrawer.tsx @@ -1,6 +1,5 @@ import { Drawer } from '@/components/shared/Drawer' import React from 'react' -import { useVoucherInstructionsContext } from '../VoucherInstructionsContext' import { PurposeQueries } from '@/api/purpose' import { InformationContainer } from '@pagopa/interop-fe-commons' import { Trans, useTranslation } from 'react-i18next' @@ -11,17 +10,18 @@ import { useQuery } from '@tanstack/react-query' type VoucherInstructionsStep1CurrentIdsDrawerProps = { isOpen: boolean onClose: VoidFunction + clientId: string + purposeId: string } export const VoucherInstructionsStep1CurrentIdsDrawer: React.FC< VoucherInstructionsStep1CurrentIdsDrawerProps -> = ({ isOpen, onClose }) => { +> = ({ isOpen, onClose, clientId, purposeId }) => { const { t } = useTranslation('voucher', { keyPrefix: 'step1.currentIdsDrawer' }) - const { clientId, selectedPurposeId } = useVoucherInstructionsContext() const { data: purpose } = useQuery({ - ...PurposeQueries.getSingle(selectedPurposeId!), - enabled: Boolean(selectedPurposeId), + ...PurposeQueries.getSingle(purposeId || ''), + enabled: Boolean(purposeId), }) return ( diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/index.ts b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep1/index.ts similarity index 100% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/index.ts rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep1/index.ts diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep2.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep2.tsx similarity index 91% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep2.tsx rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep2.tsx index 5eb981939..47639d409 100644 --- a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep2.tsx +++ b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep2.tsx @@ -10,6 +10,7 @@ import { InformationContainer } from '@pagopa/interop-fe-commons' import { useVoucherInstructionsContext } from './VoucherInstructionsContext' import ArrowForwardIcon from '@mui/icons-material/ArrowForward' import ArrowBackIcon from '@mui/icons-material/ArrowBack' +import { useSearchParams } from 'react-router-dom' const CLIENT_ASSERTION_TYP = 'JWT' const CLIENT_ASSERTION_ALG = 'RS256' @@ -17,9 +18,13 @@ const CLIENT_ASSERTION_ALG = 'RS256' export const VoucherInstructionsStep2: React.FC = () => { const { t } = useTranslation('voucher') const clientKind = useClientKind() + const [searchParams] = useSearchParams() - const { selectedPurposeId, selectedKeyId, clientId, goToNextStep, goToPreviousStep } = - useVoucherInstructionsContext() + const { goToNextStep, goToPreviousStep } = useVoucherInstructionsContext() + + const clientId = searchParams.get('clientId') || '' + const purposeId = searchParams.get('purposeId') || '' + const keyId = searchParams.get('keyId') || '' const filename = clientKind === 'CONSUMER' ? 'create_client_assertion.py' : 'create_m2m_client_assertion.py' @@ -49,9 +54,9 @@ export const VoucherInstructionsStep2: React.FC = () => { @@ -107,13 +112,13 @@ export const VoucherInstructionsStep2: React.FC = () => { tooltipTitle: t('step2.assertionPayload.audField.copySuccessFeedbackText'), }} /> - {clientKind === 'CONSUMER' && selectedPurposeId && ( + {clientKind === 'CONSUMER' && Boolean(purposeId) && ( @@ -170,13 +175,13 @@ export const VoucherInstructionsStep2: React.FC = () => { }, ]} scriptSubstitutionValues={{ - INSERISCI_VALORE_KID: selectedKeyId!, + INSERISCI_VALORE_KID: keyId, INSERISCI_VALORE_ALG: CLIENT_ASSERTION_ALG, INSERISCI_VALORE_TYP: CLIENT_ASSERTION_TYP, - INSERISCI_VALORE_ISS: clientId, - INSERISCI_VALORE_SUB: clientId, + INSERISCI_VALORE_ISS: clientId!, + INSERISCI_VALORE_SUB: clientId!, INSERISCI_VALORE_AUD: CLIENT_ASSERTION_JWT_AUDIENCE, - INSERISCI_VALORE_PUR: selectedPurposeId ?? '', + INSERISCI_VALORE_PUR: purposeId, }} /> diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep3.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep3.tsx similarity index 95% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep3.tsx rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep3.tsx index 99d295f49..b18be04bb 100644 --- a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep3.tsx +++ b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep3.tsx @@ -11,6 +11,7 @@ import { Link } from '@/router' import { useVoucherInstructionsContext } from './VoucherInstructionsContext' import ArrowForwardIcon from '@mui/icons-material/ArrowForward' import ArrowBackIcon from '@mui/icons-material/ArrowBack' +import { useSearchParams } from 'react-router-dom' const CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer' const GRANT_TYPE = 'client_credentials' @@ -18,8 +19,11 @@ const GRANT_TYPE = 'client_credentials' export const VoucherInstructionsStep3: React.FC = () => { const { t } = useTranslation('voucher') const clientKind = useClientKind() + const [searchParams] = useSearchParams() - const { clientId, goToPreviousStep, goToNextStep } = useVoucherInstructionsContext() + const { goToPreviousStep, goToNextStep } = useVoucherInstructionsContext() + + const clientId = searchParams.get('clientId') || '' return ( <> diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep4.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep4.tsx similarity index 95% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep4.tsx rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep4.tsx index 013dd3e35..ef9a61488 100644 --- a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep4.tsx +++ b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/VoucherInstructionsStep4.tsx @@ -17,15 +17,19 @@ import { apiSignalhubPushLink, apiSignalhubPullLink, } from '@/config/constants' +import { useSearchParams } from 'react-router-dom' export const VoucherInstructionsStep4: React.FC = () => { const { t } = useTranslation('voucher') const clientKind = useClientKind() - const { selectedPurposeId, goToPreviousStep } = useVoucherInstructionsContext() + const { goToPreviousStep } = useVoucherInstructionsContext() + const [searchParams] = useSearchParams() + + const purposeId = searchParams.get('purposeId') || '' const { data: purpose } = useQuery({ - ...PurposeQueries.getSingle(selectedPurposeId!), - enabled: Boolean(selectedPurposeId), + ...PurposeQueries.getSingle(purposeId), + enabled: Boolean(purposeId), }) const eserviceName = purpose ? purpose.eservice.name : '' diff --git a/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/__test__/VoucherInstructions.test.tsx b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/__test__/VoucherInstructions.test.tsx new file mode 100644 index 000000000..102a0221d --- /dev/null +++ b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/__test__/VoucherInstructions.test.tsx @@ -0,0 +1,35 @@ +import { MemoryRouter } from 'react-router-dom' +import { VoucherInstructions } from '../VoucherInstructions' +import { screen } from '@testing-library/react' +import { renderWithApplicationContext } from '@/utils/testing.utils' + +describe('VoucherInstructions testing', () => { + it('should render instruction for get consumer voucher simulation', () => { + renderWithApplicationContext( + + + , + { + withReactQueryContext: true, + } + ) + + expect(screen.getByText('step1.description')).toBeInTheDocument() + expect(screen.getByLabelText('step1.clientSelectInput.label')).toBeInTheDocument() + expect(screen.getByLabelText('step1.purposeSelectInput.label')).toBeInTheDocument() + }) + + it('should render instruction for get api voucher simulation', () => { + renderWithApplicationContext( + + + , + { + withReactQueryContext: true, + } + ) + + expect(screen.getByText('step1.description')).toBeInTheDocument() + expect(screen.getByLabelText('step1.clientSelectInput.label')).toBeInTheDocument() + }) +}) diff --git a/src/pages/ConsumerClientManagePage/components/VoucherInstructions/index.ts b/src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/index.ts similarity index 100% rename from src/pages/ConsumerClientManagePage/components/VoucherInstructions/index.ts rename to src/pages/ConsumerSimulateGetVoucherPage/components/VoucherInstructions/index.ts diff --git a/src/pages/ConsumerSimulateGetVoucherPage/index.ts b/src/pages/ConsumerSimulateGetVoucherPage/index.ts new file mode 100644 index 000000000..0a3c36afb --- /dev/null +++ b/src/pages/ConsumerSimulateGetVoucherPage/index.ts @@ -0,0 +1 @@ +export { default as ConsumerSimulateGetVoucherPage } from './ConsumerSimulateGetVoucherPage.page' diff --git a/src/pages/DeveloperToolsPage/DeveloperTools.page.tsx b/src/pages/DeveloperToolsPage/DeveloperTools.page.tsx index 1a90d27b6..7232d344b 100644 --- a/src/pages/DeveloperToolsPage/DeveloperTools.page.tsx +++ b/src/pages/DeveloperToolsPage/DeveloperTools.page.tsx @@ -61,6 +61,26 @@ const DeveloperToolsPage: React.FC = () => { + + + + + {t('sectionVoucherSimulation.firstButton')} + + + {t('sectionVoucherSimulation.secondButton')} + + + +
) diff --git a/src/router/routes.tsx b/src/router/routes.tsx index e4c9b90cd..2c5a0f415 100644 --- a/src/router/routes.tsx +++ b/src/router/routes.tsx @@ -67,6 +67,7 @@ import { ConsumerPurposeTemplateDetailsPage } from '@/pages/ConsumerPurposeTempl import ConsumerPurposeTemplateCatalogDetailsPage from '@/pages/ConsumerPurposeTemplateCatalogDetailsPage/ConsumerPurposeTemplateCatalogDetailsPage' import { ConsumerPurposeTemplateSummaryPage } from '@/pages/ConsumerPurposeTemplateSummaryPage' import { ConsumerPurposeTemplateEditPage } from '@/pages/ConsumerPurposeTemplateEditPage' +import { ConsumerSimulateGetVoucherPage } from '@/pages/ConsumerSimulateGetVoucherPage' export const { routes, reactRouterDOMRoutes, hooks, components, utils } = new InteropRouterBuilder< LangCode, @@ -387,6 +388,22 @@ export const { routes, reactRouterDOMRoutes, hooks, components, utils } = new In hideSideNav: false, authLevels: ['admin', 'support', 'api', 'security'], }) + .addRoute({ + key: 'SIMULATE_GET_VOUCHER_API', + path: '/tool-sviluppo/api-interop/simulazione-voucher', + element: , + public: false, + hideSideNav: true, + authLevels: ['admin', 'support', 'api', 'security'], + }) + .addRoute({ + key: 'SIMULATE_GET_VOUCHER_CONSUMER', + path: '/tool-sviluppo/api-e-service/simulazione-voucher', + element: , + public: false, + hideSideNav: true, + authLevels: ['admin', 'support', 'api', 'security'], + }) .addRoute({ key: 'TENANT', path: '/aderente', diff --git a/src/static/locales/en/common.json b/src/static/locales/en/common.json index ff3496b9d..5654efd42 100644 --- a/src/static/locales/en/common.json +++ b/src/static/locales/en/common.json @@ -56,7 +56,8 @@ "markAsRead": "Mark as read", "marsAsNotRead": "Mark as not read", "specifyProcessing": "Specify processing", - "save": "Save" + "save": "Save", + "simulateVoucher": "Simulate obtaining the voucher" }, "table": { "headData": { diff --git a/src/static/locales/en/developer-tools.json b/src/static/locales/en/developer-tools.json index 55b531f97..5c87fe649 100644 --- a/src/static/locales/en/developer-tools.json +++ b/src/static/locales/en/developer-tools.json @@ -15,6 +15,12 @@ "title": "Debug Client Assertion", "description": "Tool to generate, view, and validate client assertion JWT, useful for debugging and integration with OAuth 2.0 flows.", "button": "Run debug" + }, + "sectionVoucherSimulation": { + "title": "Simulate getting a voucher", + "description": "Through this feature you can simulate obtaining a voucher. Once you have selected the parameters, you can perform a client assertion, request a voucher and verify that it is compatible.", + "firstButton": "E-service simulation", + "secondButton": "Interop simulation" } } }, diff --git a/src/static/locales/en/pages.json b/src/static/locales/en/pages.json index 36371066e..b5510d155 100644 --- a/src/static/locales/en/pages.json +++ b/src/static/locales/en/pages.json @@ -35,6 +35,10 @@ "title": "Client Assertion Debug", "description": "The debug tool allows you to highlight any anomalies contained in your client assertion necessary for obtaining an access token." }, + "consumerSimulateGetVoucher": { + "title": "Simulate getting a voucher", + "description": "Through this feature you can simulate obtaining a voucher. Once you have selected the parameters, you can perform a client assertion, request a voucher and verify that it is compatible." + }, "tenantCertifier": { "title": "Party Certifier", "description": "Create and manage certified attributes and assign them to compliant entities." diff --git a/src/static/locales/en/shared-components.json b/src/static/locales/en/shared-components.json index 90b7b10e6..1305f70bb 100644 --- a/src/static/locales/en/shared-components.json +++ b/src/static/locales/en/shared-components.json @@ -510,6 +510,8 @@ "SUBSCRIBE_PURPOSE_TEMPLATE_CATALOG_DETAILS": "Template Details", "SUBSCRIBE_PURPOSE_TEMPLATE_EDIT": "Edit Purpose Template", "SUBSCRIBE_PURPOSE_CREATE_FROM_TEMPLATE": "Create Purpose from Template", - "SUBSCRIBE_PURPOSE_FROM_TEMPLATE_EDIT": "Edit Purpose from Template" + "SUBSCRIBE_PURPOSE_FROM_TEMPLATE_EDIT": "Edit Purpose from Template", + "SIMULATE_GET_VOUCHER_API": "Simulate getting a voucher", + "SIMULATE_GET_VOUCHER_CONSUMER": "Simulate getting a voucher" } } diff --git a/src/static/locales/en/voucher.json b/src/static/locales/en/voucher.json index 56728435a..1a5139650 100644 --- a/src/static/locales/en/voucher.json +++ b/src/static/locales/en/voucher.json @@ -19,6 +19,9 @@ "CONSUMER": "Select purpose and public key" }, "description": "Used to show you the correct parameters to populate the client assertion", + "clientSelectInput": { + "label": "Choose the client to use" + }, "purposeSelectInput": { "label": "Choose the purpose to use" }, diff --git a/src/static/locales/it/common.json b/src/static/locales/it/common.json index 6f63d7e83..a30d37c08 100644 --- a/src/static/locales/it/common.json +++ b/src/static/locales/it/common.json @@ -56,7 +56,8 @@ "markAsRead": "Segna come letto", "marsAsNotRead": "Segna da leggere", "specifyProcessing": "Indica trattamento", - "save": "Salva" + "save": "Salva", + "simulateVoucher": "Simula l'ottenimento del voucher" }, "table": { "headData": { diff --git a/src/static/locales/it/developer-tools.json b/src/static/locales/it/developer-tools.json index e46af6c94..5a15853d0 100644 --- a/src/static/locales/it/developer-tools.json +++ b/src/static/locales/it/developer-tools.json @@ -15,6 +15,12 @@ "title": "Debug Client Assertion", "description": "Strumento per generare, visualizzare e validare client assertion JWT, utile per il debug e l’integrazione con flussi OAuth 2.0.", "button": "Effettua il debug" + }, + "sectionVoucherSimulation": { + "title": "Simula l’ottenimento di un voucher", + "description": "Attraverso questa funzionalità potrai simulare l’ottenimento di un voucher. Una volta selezionati i parametri, potrai effettuare una client assertion, richiedere un voucher e verificare che sia compatibile.", + "firstButton": "Simulazione per e-service", + "secondButton": "Simulazione per interoperabilità" } } }, diff --git a/src/static/locales/it/pages.json b/src/static/locales/it/pages.json index 1238e6b5b..a2c09312e 100644 --- a/src/static/locales/it/pages.json +++ b/src/static/locales/it/pages.json @@ -35,6 +35,10 @@ "title": "Debug client assertion", "description": "Lo strumento di debug ti consente di evidenziare eventuali anomalie contenute nella tua client assertion necessaria per l’ottenimento dell’access token." }, + "consumerSimulateGetVoucher": { + "title": "Simula l’ottenimento di un voucher", + "description": "Attraverso questa funzionalità potrai simulare l’ottenimento di un voucher. Una volta selezionati i parametri, potrai effettuare una client assertion, richiedere un voucher e verificare che sia compatibile." + }, "tenantCertifier": { "title": "Ente certificatore", "description": "Crea e gestisci gli attributi certificati e assegnali agli enti conformi." diff --git a/src/static/locales/it/shared-components.json b/src/static/locales/it/shared-components.json index 9460bcf93..cdf49ad0a 100644 --- a/src/static/locales/it/shared-components.json +++ b/src/static/locales/it/shared-components.json @@ -475,6 +475,8 @@ "PARTY_REGISTRY": "Anagrafica e attributi", "NOT_FOUND": "Pagina non trovata", "SUBSCRIBE_DEBUG_VOUCHER": "Debug client assertion", + "SIMULATE_GET_VOUCHER_API": "Simula l’ottenimento di un voucher", + "SIMULATE_GET_VOUCHER_CONSUMER": "Simula l’ottenimento di un voucher", "ASSISTENCE_PARTY_SELECTION": "Selezione ente", "ASSISTENCE_PARTY_SELECTION_ERROR": "Errore selezione ente", "DEFAULT": "", diff --git a/src/static/locales/it/voucher.json b/src/static/locales/it/voucher.json index e7f4521cb..5e79c335a 100644 --- a/src/static/locales/it/voucher.json +++ b/src/static/locales/it/voucher.json @@ -19,6 +19,9 @@ "CONSUMER": "Seleziona finalità e chiave pubblica" }, "description": "Serve a mostrarti i parametri corretti per valorizzare la client assertion", + "clientSelectInput": { + "label": "Scegli un client da utilizzare" + }, "purposeSelectInput": { "label": "Scegli la finalità da utilizzare" },