├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── service_maintenance.md │ └── voucher_errors.md └── workflows │ ├── ci.yml │ ├── clean.yml │ └── sonar-build-spec.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── CHANGELOG.md ├── CODEOWNERS ├── Dockerfile ├── Jenkinsfile ├── README.md ├── __mocks__ ├── data │ ├── agreement.mocks.ts │ ├── attribute.mocks.ts │ ├── client.mocks.ts │ ├── delegation.mocks.ts │ ├── eservice.mocks.ts │ ├── key.mocks.ts │ ├── one-trust-notice.mocks.ts │ ├── purpose.mocks.ts │ ├── user.mocks.ts │ └── voucher.mocks.ts ├── react-i18next.ts └── zustand.ts ├── index.html ├── maintenance-page ├── maintenance-standard.html ├── maintenance-token-down.html └── style.css ├── package-lock.json ├── package.json ├── public ├── data │ └── it │ │ ├── client-assertion.json │ │ ├── create_client_assertion.py │ │ ├── create_m2m_client_assertion.py │ │ ├── public-key.json │ │ ├── session_token_curl.txt │ │ ├── voucher-python-invoke.txt │ │ └── voucher-python-m2m-invoke.txt ├── favicon-32x32.png ├── favicon.svg ├── icons │ ├── icon-144x144.png │ ├── icon-192x192.png │ ├── icon-256x256.png │ ├── icon-384x384.png │ ├── icon-48x48.png │ ├── icon-512x512.png │ ├── icon-72x72.png │ └── icon-96x96.png ├── manifest.webmanifest └── robots.txt ├── scripts ├── guide-crawl │ └── index.js ├── help │ ├── data │ │ └── it │ │ │ ├── client-assertion.md │ │ │ ├── public-key.md │ │ │ └── tos.md │ └── markdown-to-html.js └── open-api-type-generator.js ├── setupTests.ts ├── sonar-project.properties ├── src ├── App.tsx ├── api │ ├── agreement │ │ ├── agreement.downloads.ts │ │ ├── agreement.mutations.ts │ │ ├── agreement.queries.ts │ │ ├── agreement.services.ts │ │ └── index.ts │ ├── api.generatedTypes.ts │ ├── api.types.ts │ ├── attribute │ │ ├── attribute.mutations.ts │ │ ├── attribute.queries.ts │ │ ├── attribute.services.ts │ │ └── index.ts │ ├── auth │ │ ├── auth.hooks.ts │ │ ├── auth.mutations.ts │ │ ├── auth.queries.ts │ │ ├── auth.services.ts │ │ ├── auth.utils.ts │ │ └── index.ts │ ├── client │ │ ├── client.downloads.ts │ │ ├── client.mutations.ts │ │ ├── client.queries.ts │ │ ├── client.services.ts │ │ └── index.ts │ ├── delegation │ │ ├── delegation.downloads.ts │ │ ├── delegation.mutations.ts │ │ ├── delegation.queries.ts │ │ ├── delegation.services.ts │ │ └── index.ts │ ├── eservice │ │ ├── eservice.downloads.ts │ │ ├── eservice.mutations.ts │ │ ├── eservice.queries.ts │ │ ├── eservice.services.ts │ │ └── index.ts │ ├── hooks │ │ ├── index.ts │ │ └── useDownloadFile.ts │ ├── keychain │ │ ├── index.ts │ │ ├── keychain.downloads.ts │ │ ├── keychain.mutations.ts │ │ ├── keychain.queries.ts │ │ └── keychain.services.ts │ ├── maintenance │ │ ├── index.ts │ │ ├── maintenance.queries.ts │ │ └── maintenance.services.ts │ ├── one-trust-notices │ │ ├── index.ts │ │ ├── one-trust-notices.mutations.ts │ │ ├── one-trust-notices.queries.ts │ │ └── one-trust-notices.services.ts │ ├── purpose │ │ ├── index.ts │ │ ├── purpose.downloads.ts │ │ ├── purpose.mutations.ts │ │ ├── purpose.queries.ts │ │ └── purpose.services.ts │ ├── selfcare │ │ ├── index.ts │ │ ├── selfcare.queries.ts │ │ └── selfcare.services.ts │ ├── template │ │ ├── index.ts │ │ ├── template.downloads.ts │ │ ├── template.mutations.ts │ │ ├── template.queries.ts │ │ └── template.services.ts │ ├── tenant │ │ ├── index.ts │ │ ├── tenant.hooks.ts │ │ ├── tenant.mutations.ts │ │ ├── tenant.queries.ts │ │ └── tenant.services.ts │ └── voucher │ │ ├── index.ts │ │ ├── voucher.mutations.ts │ │ └── voucher.services.ts ├── components │ ├── dialogs │ │ ├── Dialog.tsx │ │ ├── DialogAcceptDelegation.tsx │ │ ├── DialogAttributeDetails.tsx │ │ ├── DialogBasic.tsx │ │ ├── DialogClonePurpose │ │ │ ├── DialogClonePurpose.tsx │ │ │ └── DialogClonePurposeEServiceAutocomplete.tsx │ │ ├── DialogCreateAgreementDraft │ │ │ ├── DialogCreateAgreementDraft.tsx │ │ │ └── DialogCreateAgreementDraftAutocomplete.tsx │ │ ├── DialogDeleteOperator.tsx │ │ ├── DialogDeleteProducerKeychainKey.tsx │ │ ├── DialogError.tsx │ │ ├── DialogRejectAgreement.tsx │ │ ├── DialogRejectDelegatedVersionDraft.tsx │ │ ├── DialogRejectDelegation.tsx │ │ ├── DialogRejectPurposeVersion.tsx │ │ ├── DialogRemoveOperatorFromClient.tsx │ │ ├── DialogRemoveUserFromKeychain.tsx │ │ ├── DialogRevokeCertifiedAttribute.tsx │ │ ├── DialogRevokeDelegation.tsx │ │ ├── DialogSessionExpired.tsx │ │ ├── DialogSetTenantMail.tsx │ │ ├── DialogTenantKindEserviceTemplate.tsx │ │ ├── DialogUpgradeAgreementVersion.tsx │ │ ├── __tests__ │ │ │ └── DialogSessionExpired.test.tsx │ │ └── index.ts │ ├── layout │ │ ├── AppLayout.tsx │ │ ├── Breadcrumbs.tsx │ │ ├── Footer.tsx │ │ ├── Header.tsx │ │ ├── LoadingOverlay.tsx │ │ ├── SideNav │ │ │ ├── CollapsableSideNavItem.tsx │ │ │ ├── SideNav.tsx │ │ │ ├── SideNavItemLink.tsx │ │ │ ├── __tests__ │ │ │ │ ├── CollapsableSideNavItem.test.tsx │ │ │ │ └── SideNav.test.tsx │ │ │ ├── hooks │ │ │ │ ├── __tests__ │ │ │ │ │ └── useGetSideNavItems.test.ts │ │ │ │ ├── useGetSideNavItems.ts │ │ │ │ └── useIsRouteInCurrentSubtree.tsx │ │ │ └── index.ts │ │ ├── ToastNotification.tsx │ │ ├── containers │ │ │ ├── AttributeContainer.tsx │ │ │ ├── AttributeGroupContainer.tsx │ │ │ ├── DocumentContainer.tsx │ │ │ ├── PageBottomActionsContainer.tsx │ │ │ ├── PageBottomCardContainer.tsx │ │ │ ├── PageContainer.tsx │ │ │ ├── SectionContainer.tsx │ │ │ ├── __tests__ │ │ │ │ ├── AttributeGroupContainer.test.tsx │ │ │ │ └── DocumentContainer.test.tsx │ │ │ └── index.ts │ │ └── index.ts │ └── shared │ │ ├── Accordion.tsx │ │ ├── ActionMenu.tsx │ │ ├── AddAttributesToForm │ │ ├── AddAttributesToForm.tsx │ │ ├── AttributeGroup.tsx │ │ └── index.ts │ │ ├── AddOperatorsToClientDrawer.tsx │ │ ├── AddUsersToKeychainDrawer.tsx │ │ ├── ApiInfoSection.tsx │ │ ├── AttributeAutocomplete.tsx │ │ ├── ByDelegationChip.tsx │ │ ├── CatalogCard.tsx │ │ ├── ClientTable │ │ ├── ClientTable.tsx │ │ ├── ClientTableRow.tsx │ │ ├── __tests__ │ │ │ └── ClientTableRow.test.tsx │ │ └── index.ts │ │ ├── CreateAttributeDrawer.tsx │ │ ├── CreateStepPurposeRiskAnalysisForm.tsx │ │ ├── DelegationTable │ │ ├── DelegationsTable.tsx │ │ ├── DelegationsTableRow.tsx │ │ └── index.ts │ │ ├── DownloadableDocumentsList.tsx │ │ ├── Drawer.tsx │ │ ├── EServiceVersionSelectorDrawer.tsx │ │ ├── ErrorBoundary.tsx │ │ ├── EserviceTemplate │ │ ├── EServiceTemplateAttributes.tsx │ │ ├── EServiceTemplateDocumentationSection.tsx │ │ ├── EServiceTemplateGeneralInfoSection.tsx │ │ ├── EServiceTemplateTechnicalInfoSection.tsx │ │ ├── EServiceTemplateThresholdsSection.tsx │ │ ├── EServiceTemplateUpdateDocumentationDrawer.tsx │ │ ├── EServiceTemplateUsefulLinksSection.tsx │ │ ├── EServiceTemplateVersionSelectorDrawer.tsx │ │ └── index.ts │ │ ├── FirstLoadingSpinner.tsx │ │ ├── HeadSection │ │ ├── ActionsButtons.tsx │ │ ├── HeadSection.tsx │ │ ├── __tests__ │ │ │ └── HeadSection.test.tsx │ │ └── index.ts │ │ ├── IconLink.tsx │ │ ├── InfoTooltip.tsx │ │ ├── InputWrapper.tsx │ │ ├── KeychainsTable │ │ ├── KeychainsTable.tsx │ │ ├── KeychainsTableRow.tsx │ │ └── index.ts │ │ ├── MUI-skeletons │ │ ├── ButtonSkeleton.tsx │ │ ├── TabListSkeleton.tsx │ │ └── index.ts │ │ ├── MaintenanceBanner.tsx │ │ ├── ReadOnlyDescriptorAttributes.tsx │ │ ├── RejectReasonDrawer.tsx │ │ ├── RiskAnalysisFormComponents │ │ ├── RiskAnalysisCheckboxGroup.tsx │ │ ├── RiskAnalysisFormComponents.tsx │ │ ├── RiskAnalysisInputWrapper.tsx │ │ ├── RiskAnalysisRadioGroup.tsx │ │ ├── RiskAnalysisSelect.tsx │ │ ├── RiskAnalysisSwitch.tsx │ │ ├── RiskAnalysisTextField.tsx │ │ ├── __test__ │ │ │ └── RiskAnalysisSwitch.test.tsx │ │ └── index.ts │ │ ├── RiskAnalysisInfoSummary.tsx │ │ ├── StatusChip.tsx │ │ ├── StepActions.tsx │ │ ├── Stepper.tsx │ │ ├── SummaryAccordion.tsx │ │ ├── UpdateAttributesDrawer.tsx │ │ ├── UpdateDescriptionDrawer.tsx │ │ ├── UpdateNameDrawer.tsx │ │ ├── UpdateThresholdsDrawer.tsx │ │ ├── __tests__ │ │ ├── ActionMenu.test.tsx │ │ ├── CatalogCard.test.tsx │ │ ├── Drawer.test.tsx │ │ └── InfoTooltip.test.tsx │ │ └── react-hook-form-inputs │ │ ├── RHFAutocomplete │ │ ├── RHFAutocompleteMultiple.tsx │ │ ├── RHFAutocompleteSingle.tsx │ │ ├── _RHFAutocompleteBase.tsx │ │ └── index.ts │ │ ├── RHFCheckbox.tsx │ │ ├── RHFCheckboxGroup.tsx │ │ ├── RHFRadioGroup.tsx │ │ ├── RHFSingleFileInput.tsx │ │ ├── RHFSwitch.tsx │ │ ├── RHFTextField.tsx │ │ ├── __tests__ │ │ ├── RHFCheckbox.test.tsx │ │ ├── RHFCheckboxGroup.test.tsx │ │ ├── RHFRadioGroup.test.tsx │ │ ├── RHFSingleFileInput.test.tsx │ │ ├── RHFSwitch.test.tsx │ │ ├── RHFTextField.test.tsx │ │ └── test-utils.tsx │ │ └── index.ts ├── config │ ├── axios.ts │ ├── constants.ts │ ├── env.ts │ ├── query-client.ts │ ├── react-i18next.ts │ └── tracking.ts ├── hooks │ ├── __tests__ │ │ ├── useActiveStep.test.tsx │ │ ├── useActiveTab.test.tsx │ │ ├── useCheckRiskAnalysisVersionMismatch.test.ts │ │ ├── useClientKind.test.ts │ │ ├── useDescriptorAttributesPartyOwnership.test.tsx │ │ ├── useDrawerState.test.tsx │ │ ├── useGetAgreementsActions.test.ts │ │ ├── useGetClientActions.test.ts │ │ ├── useGetConsumerPurposesActions.test.ts │ │ ├── useGetEServiceConsumerActions.test.ts │ │ ├── useGetKeyActions.test.tsx │ │ ├── useGetProducerDelegationUserRole.test.ts │ │ ├── useGetProviderEServiceActions.test.ts │ │ ├── useGetProviderPurposesActions.test.ts │ │ ├── useMaintenanceBanner.test.ts │ │ └── useResolveError.test.tsx │ ├── useActiveStep.ts │ ├── useActiveTab.ts │ ├── useCheckRiskAnalysisVersionMismatch.ts │ ├── useClientKind.ts │ ├── useCurrentLanguage.ts │ ├── useDescriptorAttributesPartyOwnership.ts │ ├── useDrawerState.ts │ ├── useGetAgreementsActions.ts │ ├── useGetClientActions.ts │ ├── useGetClientOperatorsActions.ts │ ├── useGetConsumerPurposesActions.ts │ ├── useGetDelegationActions.ts │ ├── useGetEServiceConsumerActions.ts │ ├── useGetKeyActions.ts │ ├── useGetKeychainActions.ts │ ├── useGetProducerDelegationUserRole.ts │ ├── useGetProviderEServiceActions.ts │ ├── useGetProviderEServiceTemplateActions.ts │ ├── useGetProviderPurposesActions.ts │ ├── useMaintenanceBanner.ts │ ├── useResolveError.tsx │ └── useRiskAnalysisForm.ts ├── index.css ├── main.tsx ├── pages │ ├── AssistanceTenantSelectionErrorPage │ │ ├── AssistanceTenantSelectionError.page.tsx │ │ └── index.ts │ ├── AssistanceTenantSelectionPage │ │ ├── AssistanceTenantSelection.page.tsx │ │ ├── components │ │ │ ├── TenantSelect.tsx │ │ │ └── TenantSelectItem.tsx │ │ └── index.ts │ ├── ConsumerAgreementCreatePage │ │ ├── ConsumerAgreementCreate.page.tsx │ │ ├── ConsumerAgreementCreateContentContext.tsx │ │ ├── components │ │ │ ├── ConsumerAgreementCreateAgreementGeneralInformation.tsx │ │ │ ├── ConsumerAgreementCreateCertifiedAttributesDrawer.tsx │ │ │ ├── ConsumerAgreementCreateContent.tsx │ │ │ ├── ConsumerAgreementCreateDeclaredAttributesSection.tsx │ │ │ ├── ConsumerAgreementCreateVerifiedAttributesSection.tsx │ │ │ ├── ConsumerAgreementDocsInputSection.tsx │ │ │ └── ConsumerNotesInputSection.tsx │ │ ├── hooks │ │ │ ├── __test__ │ │ │ │ └── useGetConsumerAgreementCreateAlertProps.test.ts │ │ │ ├── useGetConsumerAgreementCreateAlertProps.ts │ │ │ └── useGetConsumerDeclaredAttributesActions.ts │ │ └── index.ts │ ├── ConsumerAgreementDetailsPage │ │ ├── ConsumerAgreementDetails.page.tsx │ │ ├── components │ │ │ ├── ConsumerAgreementDetailsAttributesSectionsList │ │ │ │ ├── ConsumerAgreementDetailsAttributesSectionsList.tsx │ │ │ │ ├── ConsumerAgreementDetailsCertifiedAttributesSection.tsx │ │ │ │ ├── ConsumerAgreementDetailsDeclaredAttributesSection.tsx │ │ │ │ └── ConsumerAgreementDetailsVerifiedAttributesSection │ │ │ │ │ ├── ConsumerAgreementDetailsDocumentationDrawer.tsx │ │ │ │ │ └── ConsumerAgreementDetailsVerifiedAttributesSection.tsx │ │ │ ├── ConsumerAgreementDetailsContext.tsx │ │ │ └── ConsumerAgreementDetailsGeneralInfoSection │ │ │ │ ├── ConsumerAgreementDetailsCertifiedAttributesDrawer.tsx │ │ │ │ ├── ConsumerAgreementDetailsContactDrawer.tsx │ │ │ │ └── ConsumerAgreementDetailsGeneralInfoSection.tsx │ │ ├── hooks │ │ │ ├── __test__ │ │ │ │ └── useGetConsumerAgreementAlertProps.test.ts │ │ │ └── useGetConsumerAgreementAlertProps.ts │ │ └── index.ts │ ├── ConsumerAgreementsListPage │ │ ├── ConsumerAgreementsList.page.tsx │ │ ├── components │ │ │ ├── ConsumerAgreementsTable.tsx │ │ │ ├── ConsumerAgreementsTableRow.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── ConsumerClientCreatePage │ │ ├── ConsumerClientCreate.page.tsx │ │ ├── components │ │ │ └── OperatorsInputTable.tsx │ │ └── index.ts │ ├── ConsumerClientListPage │ │ ├── ConsumerClientList.page.tsx │ │ └── index.ts │ ├── ConsumerClientM2MListPage │ │ ├── ConsumerClientM2MList.page.tsx │ │ └── index.ts │ ├── ConsumerClientManagePage │ │ ├── ConsumerClientManage.page.tsx │ │ ├── components │ │ │ ├── ClientOperators │ │ │ │ ├── ClientOperators.tsx │ │ │ │ ├── ClientOperatorsTable.tsx │ │ │ │ ├── ClientOperatorsTableRow.tsx │ │ │ │ └── index.ts │ │ │ ├── ClientPublicKeys │ │ │ │ ├── ClientAddPublicKeyDrawer.tsx │ │ │ │ ├── ClientPublicKeys.tsx │ │ │ │ ├── ClientPublicKeysHeadSection.tsx │ │ │ │ ├── ClientPublicKeysTable.tsx │ │ │ │ ├── ClientPublicKeysTableRow.tsx │ │ │ │ └── index.ts │ │ │ ├── SetClientAdminDrawer │ │ │ │ └── SetClientAdminDrawer.tsx │ │ │ └── VoucherInstructions │ │ │ │ ├── ClientVoucherIntructionsPurposeSelect.tsx │ │ │ │ ├── CodeSnippetPreview.tsx │ │ │ │ ├── VoucherInstructions.tsx │ │ │ │ ├── VoucherInstructionsContext.tsx │ │ │ │ ├── VoucherInstructionsStep1 │ │ │ │ ├── VoucherInstructionsStep1.tsx │ │ │ │ ├── VoucherInstructionsStep1CurrentIdsDrawer.tsx │ │ │ │ └── index.ts │ │ │ │ ├── VoucherInstructionsStep2.tsx │ │ │ │ ├── VoucherInstructionsStep3.tsx │ │ │ │ ├── VoucherInstructionsStep4.tsx │ │ │ │ └── index.ts │ │ └── index.ts │ ├── ConsumerDebugVoucherPage │ │ ├── ConsumerDebugVoucher.page.tsx │ │ ├── DebugVoucherContext.tsx │ │ ├── __test__ │ │ │ ├── ConsumerDebugVoucher.page.test.tsx │ │ │ └── DebugVoucherContext.test.tsx │ │ ├── components │ │ │ ├── DebugVoucherForm.tsx │ │ │ ├── DebugVoucherResults.tsx │ │ │ ├── DebugVoucherResultsAlert.tsx │ │ │ ├── DebugVoucherResultsRequestSection.tsx │ │ │ ├── DebugVoucherResultsStep.tsx │ │ │ ├── DebugVoucherResultsStepsSection.tsx │ │ │ ├── DebugVoucherStepDrawer.tsx │ │ │ └── __test__ │ │ │ │ ├── DebugVoucherForm.test.tsx │ │ │ │ ├── DebugVoucherResults.test.tsx │ │ │ │ ├── DebugVoucherResultsAlert.test.tsx │ │ │ │ ├── DebugVoucherResultsStep.test.tsx │ │ │ │ ├── DebugVoucherResultsStepsSection.test.tsx │ │ │ │ ├── DebugVoucherStepDrawer.test.tsx │ │ │ │ └── test.commons.ts │ │ ├── hooks │ │ │ ├── __test__ │ │ │ │ └── useGetDebugVoucherResultChipProps.test.ts │ │ │ └── useGetDebugVoucherResultChipProps.ts │ │ └── index.ts │ ├── ConsumerEServiceCatalogPage │ │ ├── ConsumerEServiceCatalog.page.tsx │ │ ├── components │ │ │ ├── EServiceCatalogGrid.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── ConsumerEServiceDetailsPage │ │ ├── ConsumerEServiceDetails.page.tsx │ │ ├── components │ │ │ ├── ConsumerEServiceDescriptorAttributes.tsx │ │ │ ├── ConsumerEServiceDetailsAlerts.tsx │ │ │ └── ConsumerEServiceGeneralInfoSection │ │ │ │ ├── ConsumerEServiceGeneralInfoSection.tsx │ │ │ │ ├── ConsumerEServiceProducerContactsDrawer.tsx │ │ │ │ ├── ConsumerEServiceTechnicalInfoDrawer.tsx │ │ │ │ └── index.ts │ │ └── index.ts │ ├── ConsumerEServiceTemplateDetailsPage │ │ ├── ConsumerEServiceTemplateDetails.page.tsx │ │ ├── components │ │ │ ├── ConsumerEServiceTemplateDetails.tsx │ │ │ └── index.ts │ │ ├── hooks │ │ │ └── useGetConsumerEServiceTemplateActions.tsx │ │ └── index.ts │ ├── ConsumerPurposeCreatePage │ │ ├── ConsumerPurposeCreate.page.tsx │ │ ├── components │ │ │ ├── PurposeCreateConsumerAutocomplete.tsx │ │ │ ├── PurposeCreateEServiceAutocomplete.tsx │ │ │ ├── PurposeCreateForm.tsx │ │ │ ├── PurposeCreateProviderRiskAnalysisAutocomplete.tsx │ │ │ └── PurposeCreateTemplateAutocomplete.tsx │ │ └── index.ts │ ├── ConsumerPurposeDetailsPage │ │ ├── ConsumerPurposeDetails.page.tsx │ │ ├── components │ │ │ ├── PurposeClientsTab │ │ │ │ ├── PurposeAddClientDrawer.tsx │ │ │ │ ├── PurposeClientsTab.tsx │ │ │ │ ├── PurposeClientsTable.tsx │ │ │ │ ├── PurposeClientsTableRow.tsx │ │ │ │ └── index.ts │ │ │ └── PurposeDetailsTab │ │ │ │ ├── ConsumerPurposeDetailsDailyCallsPlanCard.tsx │ │ │ │ ├── ConsumerPurposeDetailsDailyCallsThresholdsCard.tsx │ │ │ │ ├── ConsumerPurposeDetailsDailyCallsUpdateDrawer.tsx │ │ │ │ ├── ConsumerPurposeDetailsDailyCallsUpdatePlanCard.tsx │ │ │ │ ├── ConsumerPurposeDetailsGeneralInfoSection.tsx │ │ │ │ ├── ConsumerPurposeDetailsLoadEstimateSection.tsx │ │ │ │ ├── PurposeDetailsTab.tsx │ │ │ │ └── index.ts │ │ ├── hooks │ │ │ └── useGetPurposeStateAlertProps.ts │ │ └── index.ts │ ├── ConsumerPurposeEditPage │ │ ├── ConsumerPurposeEdit.page.tsx │ │ ├── components │ │ │ ├── PurposeEditStepGeneral │ │ │ │ ├── PurposeEditStepGeneral.tsx │ │ │ │ ├── PurposeEditStepGeneralForm.tsx │ │ │ │ └── index.ts │ │ │ └── PurposeEditStepRiskAnalysis │ │ │ │ ├── PurposeEditStepRiskAnalysis.tsx │ │ │ │ ├── RiskAnalysisForm │ │ │ │ ├── RiskAnalysisForm.tsx │ │ │ │ ├── RiskAnalysisVersionMismatchDialog.tsx │ │ │ │ ├── __tests__ │ │ │ │ │ ├── RiskAnalysisForm.test.tsx │ │ │ │ │ └── RiskAnalysisVersionMismatchDialog.test.tsx │ │ │ │ └── index.ts │ │ │ │ └── index.ts │ │ └── index.ts │ ├── ConsumerPurposeSummaryPage │ │ ├── ConsumerPurposeSummary.page.tsx │ │ ├── components │ │ │ ├── ConsumerPurposeSummaryGeneralInformationAccordion.tsx │ │ │ ├── ConsumerPurposeSummaryRiskAnalysisAccordion.tsx │ │ │ └── index.ts │ │ ├── hooks │ │ │ ├── __tests__ │ │ │ │ └── useGetConsumerPurposeAlertProps.test.ts │ │ │ └── useGetConsumerPurposeAlertProps.ts │ │ └── index.ts │ ├── ConsumerPurposesListPage │ │ ├── ConsumerPurposesList.page.tsx │ │ ├── components │ │ │ ├── ConsumerPurposesTable.tsx │ │ │ ├── ConsumerPurposesTableRow.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── DelegationCreatePage │ │ ├── DelegationCreate.page.tsx │ │ ├── components │ │ │ ├── DelegationCreateCards.tsx │ │ │ ├── DelegationCreateEServiceAutocomplete.tsx │ │ │ ├── DelegationCreateEServiceFromTemplateAutocomplete.tsx │ │ │ ├── DelegationCreateForm.tsx │ │ │ ├── DelegationCreateFormCreateEservice.tsx │ │ │ └── DelegationCreateTenantAutocomplete.tsx │ │ └── index.ts │ ├── DelegationDetailsPage │ │ ├── DelegationDetails.page.tsx │ │ ├── components │ │ │ └── DelegationGeneralInfoSection.tsx │ │ └── index.ts │ ├── DelegationsPage │ │ ├── Delegations.page.tsx │ │ ├── DelegationsAvailabilityTab │ │ │ ├── DelegationAvailabilityDrawer.tsx │ │ │ └── DelegationAvailabilityTab.tsx │ │ ├── DelegationsGrantedTab │ │ │ └── DelegationsGrantedTab.tsx │ │ ├── components │ │ │ └── DelegationsReceivedTab │ │ │ │ └── DelegationsReceivedTab.tsx │ │ └── index.ts │ ├── DeveloperToolsPage │ │ ├── DeveloperTools.page.tsx │ │ └── index.ts │ ├── ErrorPage │ │ ├── Error.page.tsx │ │ └── index.ts │ ├── KeyDetailsPage │ │ ├── KeyDetails.page.tsx │ │ ├── components │ │ │ └── KeyGeneralInfoSection.tsx │ │ └── index.ts │ ├── LogoutPage │ │ ├── Logout.page.tsx │ │ └── index.ts │ ├── NotFoundPage │ │ ├── NotFound.page.tsx │ │ └── index.ts │ ├── OperatorDetailsPage │ │ ├── OperatorDetails.page.tsx │ │ ├── components │ │ │ └── OperatorGeneralInfoSection.tsx │ │ └── index.ts │ ├── PartyRegistryPage │ │ ├── PartyRegistry.page.tsx │ │ ├── components │ │ │ ├── PartyAttributesSection │ │ │ │ ├── AttributesContainer.tsx │ │ │ │ ├── CertifiedPartyAttributes.tsx │ │ │ │ ├── DeclaredPartyAttributes.tsx │ │ │ │ ├── EmptyAttributesAlert.tsx │ │ │ │ ├── PartyAttributesSection.tsx │ │ │ │ ├── VerifiedPartyAttributes.tsx │ │ │ │ └── index.ts │ │ │ ├── PartyContactsSection │ │ │ │ ├── PartyContactsSection.tsx │ │ │ │ ├── UpdatePartyMailDrawer.tsx │ │ │ │ └── index.ts │ │ │ ├── PartyGeneralInfoSection │ │ │ │ ├── PartyGeneralInfoSection.tsx │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── PrivacyPolicyPage │ │ ├── PrivacyPolicy.page.tsx │ │ └── index.ts │ ├── ProviderAgreementDetailsPage │ │ ├── ProviderAgreementDetails.page.tsx │ │ ├── components │ │ │ ├── ProviderAgreementDetailsAttributesSectionsList │ │ │ │ ├── ProviderAgreementDetailsAttributesSectionsList.tsx │ │ │ │ ├── ProviderAgreementDetailsCertifiedAttributesSection.tsx │ │ │ │ ├── ProviderAgreementDetailsDeclaredAttributesSection.tsx │ │ │ │ └── ProviderAgreementDetailsVerifiedAttributesSection │ │ │ │ │ ├── ProviderAgreementDetailsAttributesDrawerRadioGroup.tsx │ │ │ │ │ ├── ProviderAgreementDetailsDocumentationDrawer.tsx │ │ │ │ │ ├── ProviderAgreementDetailsVerifiedAttributesDrawer.tsx │ │ │ │ │ ├── ProviderAgreementDetailsVerifiedAttributesDrawerDatePicker.tsx │ │ │ │ │ ├── ProviderAgreementDetailsVerifiedAttributesDrawerForm.tsx │ │ │ │ │ └── ProviderAgreementDetailsVerifiedAttributesSection.tsx │ │ │ ├── ProviderAgreementDetailsContext.tsx │ │ │ └── ProviderAgreementDetailsGeneralInfoSection │ │ │ │ ├── ProviderAgreementDetailsAttributesDrawer.tsx │ │ │ │ ├── ProviderAgreementDetailsAttributesDrawerCertifiedAttributesSection.tsx │ │ │ │ ├── ProviderAgreementDetailsAttributesDrawerDeclaredAttributesSection.tsx │ │ │ │ ├── ProviderAgreementDetailsContactDrawer.tsx │ │ │ │ └── ProviderAgreementDetailsGeneralInfoSection.tsx │ │ ├── hooks │ │ │ └── useProviderAgreementGetVerifiedAttributesActions.ts │ │ └── index.ts │ ├── ProviderAgreementsListPage │ │ ├── ProviderAgreementsList.page.tsx │ │ ├── components │ │ │ ├── ProviderAgreementsTable.tsx │ │ │ ├── ProviderAgreementsTableRow.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── ProviderEServiceCreatePage │ │ ├── ProviderEServiceCreate.page.tsx │ │ ├── components │ │ │ ├── EServiceCreateContext.tsx │ │ │ ├── EServiceCreateStepAttributes │ │ │ │ ├── EServiceCreateStepAttributes.tsx │ │ │ │ └── index.ts │ │ │ ├── EServiceCreateStepDocuments │ │ │ │ ├── EServiceCreateFromTemplateStepDocuments.tsx │ │ │ │ ├── EServiceCreateStepDocuments.tsx │ │ │ │ ├── EServiceCreateStepDocumentsDoc.tsx │ │ │ │ ├── EServiceCreateStepDocumentsInterface.tsx │ │ │ │ ├── EServiceEditInfoInterface.tsx │ │ │ │ └── index.ts │ │ │ ├── EServiceCreateStepGeneral │ │ │ │ ├── EServiceCreateStepGeneral.tsx │ │ │ │ └── index.ts │ │ │ ├── EServiceCreateStepPurpose │ │ │ │ ├── EServiceCreateStepPurpose.tsx │ │ │ │ ├── EServiceCreateStepPurposeAddPurposesTable.tsx │ │ │ │ └── EServiceCreateStepPurposeRiskAnalysis │ │ │ │ │ └── EServiceCreateStepPurposeRiskAnalysis.tsx │ │ │ └── EServiceCreateStepVersion │ │ │ │ ├── EServiceCreateStepVersion.tsx │ │ │ │ └── index.ts │ │ └── index.ts │ ├── ProviderEServiceDetailsPage │ │ ├── ProviderEServiceDetails.page.tsx │ │ ├── components │ │ │ ├── ProviderEServiceDetailsTab │ │ │ │ ├── ProviderEServiceDescriptorAttributes.tsx │ │ │ │ ├── ProviderEServiceDetailsAlerts.tsx │ │ │ │ ├── ProviderEServiceDetailsTab.tsx │ │ │ │ ├── ProviderEServiceGeneralInfoSection │ │ │ │ │ ├── ProviderEServiceGeneralInfoSection.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── ProviderEServiceTechnicalInfoSection │ │ │ │ │ ├── ProviderEServiceAgreementApprovalPolicySection.tsx │ │ │ │ │ ├── ProviderEServiceDocumentationSection.tsx │ │ │ │ │ ├── ProviderEServiceTechnicalInfoSection.tsx │ │ │ │ │ ├── ProviderEServiceThresholdsSection.tsx │ │ │ │ │ ├── ProviderEServiceUpdateAgreementApprovalPolicyDrawer.tsx │ │ │ │ │ ├── ProviderEServiceUpdateDocumentationDrawer.tsx │ │ │ │ │ ├── ProviderEServiceUsefulLinksSection.tsx │ │ │ │ │ └── index.ts │ │ │ └── ProviderEServiceKeychainsTab │ │ │ │ ├── AddKeychainToEServiceDrawer.tsx │ │ │ │ ├── ProviderEServiceKeychainsTab.tsx │ │ │ │ ├── ProviderEServiceKeychainsTable.tsx │ │ │ │ ├── ProviderEServiceKeychainsTableRow.tsx │ │ │ │ └── index.ts │ │ └── index.ts │ ├── ProviderEServiceFromTemplateCreatePage │ │ ├── ProviderEServiceFromTemplateCreate.page.tsx │ │ └── index.ts │ ├── ProviderEServiceListPage │ │ ├── ProviderEServiceList.page.tsx │ │ ├── components │ │ │ ├── EServiceTable.tsx │ │ │ ├── EServiceTableRow.tsx │ │ │ ├── ProviderEServiceImportVersionDrawer.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── ProviderEServiceSummaryPage │ │ ├── ProviderEServiceSummary.page.tsx │ │ ├── components │ │ │ ├── ProviderEServiceAttributeVersionSummary.tsx │ │ │ ├── ProviderEServiceDocumentationSummary.tsx │ │ │ ├── ProviderEServiceGeneralInfoSummary.tsx │ │ │ ├── ProviderEServiceRiskAnalysisSummaryList.tsx │ │ │ ├── ProviderEServiceVersionInfoSummary.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── ProviderEServiceTemplateCreatePage │ │ ├── ProviderEServiceTemplateCreate.page.tsx │ │ ├── components │ │ │ ├── EServiceTemplateCreateStepAttributes │ │ │ │ ├── EServiceTemplateCreateStepAttributes.tsx │ │ │ │ └── index.ts │ │ │ ├── EServiceTemplateCreateStepDocuments │ │ │ │ ├── EServiceTemplateCreateStepDocuments.tsx │ │ │ │ ├── EServiceTemplateCreateStepDocumentsDoc.tsx │ │ │ │ └── EServiceTemplateCreateStepDocumentsInterface.tsx │ │ │ ├── EServiceTemplateCreateStepGeneral │ │ │ │ └── EServiceTemplateCreateStepGeneral.tsx │ │ │ ├── EServiceTemplateCreateStepPurpose │ │ │ │ ├── EServiceTemplateCreateStepPurpose.tsx │ │ │ │ ├── EServiceTemplateCreateStepPurposeAddPurposeTable.tsx │ │ │ │ └── EServiceTemplateCreateStepPurposeRiskAnalysis │ │ │ │ │ └── EServiceTemplateCreateStepPurposeRiskAnalysis.tsx │ │ │ ├── EServiceTemplateCreateStepVersion │ │ │ │ ├── EServiceTemplateCreateStepVersion.tsx │ │ │ │ └── index.ts │ │ │ └── ProviderEServiceTemplateContext.tsx │ │ └── index.ts │ ├── ProviderEServiceTemplateDetailsPage │ │ ├── ProviderEServiceTemplateDetails.page.tsx │ │ ├── components │ │ │ ├── ProviderEServiceTemplateDetailsTab │ │ │ │ └── ProviderEServiceTemplateDetailsTab.tsx │ │ │ └── ProviderEServiceTemplateTenantsTab │ │ │ │ ├── ProviderEServiceTemplateTenantsTab.tsx │ │ │ │ ├── ProviderEServiceTemplateUsingTenantsTable.tsx │ │ │ │ └── ProviderEServiceTemplateUsingTenantsTableRow.tsx │ │ └── index.ts │ ├── ProviderEServiceTemplateSummaryPage │ │ ├── ProviderEServiceTemplateSummary.page.tsx │ │ ├── components │ │ │ ├── ProviderEServiceTemplateAttributeVersionSummary.tsx │ │ │ ├── ProviderEServiceTemplateDocumentationSummary.tsx │ │ │ ├── ProviderEServiceTemplateGeneralInfoSummary.tsx │ │ │ ├── ProviderEServiceTemplateRiskAnalysisSummaryList.tsx │ │ │ ├── ProviderEServiceTemplateVersionInfoSummary.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── ProviderEServiceTemplatesCatalogPage │ │ ├── ProviderEServiceTemplatesCatalog.page.tsx │ │ ├── components │ │ │ ├── EServiceTemplateCatalogGrid.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── ProviderEServiceTemplatesListPage │ │ ├── ProviderEServiceTemplatesList.page.tsx │ │ ├── components │ │ │ ├── TemplateTable.tsx │ │ │ ├── TemplateTableRow.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── ProviderKeychainCreatePage │ │ ├── ProviderKeychainCreate.page.tsx │ │ ├── components │ │ │ └── UsersInputTable.tsx │ │ └── index.ts │ ├── ProviderKeychainDetailsPage │ │ ├── ProviderKeychainDetails.page.tsx │ │ ├── components │ │ │ ├── KeychainMembersTab │ │ │ │ ├── KeychainMembersTab.tsx │ │ │ │ ├── KeychainMembersTable.tsx │ │ │ │ └── KeychainMembersTableRow.tsx │ │ │ └── KeychainPublicKeysTab │ │ │ │ ├── KeychainAddPublicKeyButton.tsx │ │ │ │ ├── KeychainAddPublicKeyDrawer.tsx │ │ │ │ ├── KeychainPublicKeysTab.tsx │ │ │ │ ├── KeychainPublicKeysTable.tsx │ │ │ │ └── KeychainPublicKeysTableRow.tsx │ │ ├── hooks │ │ │ ├── useGetProducerKeychainKeyActions.ts │ │ │ └── useGetProducerKeychainUserActions.ts │ │ └── index.ts │ ├── ProviderKeychainPublicKeyDetailsPage │ │ ├── ProviderKeychainPublicKeyDetails.page.tsx │ │ ├── components │ │ │ └── ProviderKeychainPublicKeyDetailsGeneralInfoSection.tsx │ │ └── index.ts │ ├── ProviderKeychainUserDetailsPage │ │ ├── ProviderKeychainUserDetails.page.tsx │ │ ├── components │ │ │ └── ProviderKeychainUserDetailsGeneralInfoSection.tsx │ │ └── index.ts │ ├── ProviderKeychainsListPage │ │ ├── ProviderKeychainsList.page.tsx │ │ └── index.ts │ ├── ProviderPurposeDetailsPage │ │ ├── ProviderPurposeDetails.page.tsx │ │ ├── components │ │ │ ├── ProviderPurposeDetailsDailyCallsPlanCard.tsx │ │ │ ├── ProviderPurposeDetailsDailyCallsThresholdsCard.tsx │ │ │ ├── ProviderPurposeDetailsGeneralInfoSection.tsx │ │ │ ├── ProviderPurposeDetailsLoadEstimateSection.tsx │ │ │ └── ProviderPurposeDetailsTechnicalInfoSection.tsx │ │ ├── hooks │ │ │ └── useGetPurposeStateAlertProps.ts │ │ └── index.ts │ ├── ProviderPurposesListPage │ │ ├── ProviderPurposesList.page.tsx │ │ ├── components │ │ │ ├── ProviderPurposesTable.tsx │ │ │ ├── ProviderPurposesTableRow.tsx │ │ │ └── index.ts │ │ └── index.ts │ ├── RiskAnalysisEserviceAssociatedPage │ │ ├── RiskAnalysisEServiceAssociated.page.tsx │ │ └── index.ts │ ├── RiskAnalysisExporterToolPage │ │ ├── RiskAnalysisExporterTool.page.tsx │ │ ├── components │ │ │ ├── RiskAnalysisExporterToolContext.tsx │ │ │ ├── RiskAnalysisToolFormStep.tsx │ │ │ └── RiskAnalysisToolJsonExportStep.tsx │ │ └── index.ts │ ├── TOSPage │ │ ├── TOS.page.tsx │ │ └── index.ts │ ├── TenantCertifierAttributeDetailsPage │ │ ├── TenantCertifierAttributeDetails.page.tsx │ │ └── index.ts │ ├── TenantCertifierPage │ │ ├── TenantCertifier.page.tsx │ │ ├── components │ │ │ ├── AssignAttributesTab │ │ │ │ ├── AssignAttributeDrawer.tsx │ │ │ │ ├── AssignAttributesTab.tsx │ │ │ │ ├── AttributesTable.tsx │ │ │ │ └── AttributesTableRow.tsx │ │ │ └── ManageAttributesTab │ │ │ │ ├── AttributesTable.tsx │ │ │ │ ├── AttributesTableRow.tsx │ │ │ │ ├── CreateAttributeDrawer.tsx │ │ │ │ └── ManageAttributesTab.tsx │ │ └── index.ts │ └── index.ts ├── react-i18next.d.ts ├── router │ ├── __tests__ │ │ └── router.utils.test.ts │ ├── components │ │ ├── RouterProvider.tsx │ │ └── RoutesWrapper │ │ │ ├── AuthGuard.tsx │ │ │ ├── RoutesWrapper.tsx │ │ │ ├── TOSAgreement.tsx │ │ │ ├── __tests__ │ │ │ ├── AuthGuard.test.tsx │ │ │ ├── RoutesWrapper.test.tsx │ │ │ └── TOSAgreement.test.tsx │ │ │ └── index.ts │ ├── hooks │ │ ├── __tests__ │ │ │ ├── useScrollTopOnLocationChange.test.tsx │ │ │ └── useTOSAgreement.test.ts │ │ ├── useCheckSessionExpired.ts │ │ ├── useCurrentRoute.ts │ │ ├── useScrollTopOnLocationChange.ts │ │ └── useTOSAgreement.ts │ ├── index.ts │ ├── router.utils.ts │ └── routes.tsx ├── static │ └── locales │ │ ├── en │ │ ├── agreement.json │ │ ├── assistance.json │ │ ├── attribute.json │ │ ├── client.json │ │ ├── common.json │ │ ├── developer-tools.json │ │ ├── error.json │ │ ├── eservice.json │ │ ├── key.json │ │ ├── keychain.json │ │ ├── mutations-feedback.json │ │ ├── pages.json │ │ ├── pagopa.json │ │ ├── party.json │ │ ├── purpose.json │ │ ├── shared-components.json │ │ ├── template.json │ │ ├── user.json │ │ └── voucher.json │ │ └── it │ │ ├── agreement.json │ │ ├── assistance.json │ │ ├── attribute.json │ │ ├── client.json │ │ ├── common.json │ │ ├── developer-tools.json │ │ ├── error.json │ │ ├── eservice.json │ │ ├── key.json │ │ ├── keychain.json │ │ ├── mutations-feedback.json │ │ ├── pages.json │ │ ├── pagopa.json │ │ ├── party.json │ │ ├── purpose.json │ │ ├── shared-components.json │ │ ├── template.json │ │ ├── user.json │ │ └── voucher.json ├── stores │ ├── dialog.store.ts │ ├── index.ts │ ├── loading-overlay.store.ts │ └── toast-notification.store.ts ├── types │ ├── attribute.types.ts │ ├── common.types.ts │ ├── dialog.types.ts │ ├── party.types.ts │ └── risk-analysis-form.types.ts ├── utils │ ├── __tests__ │ │ ├── agreement.utils.test.ts │ │ ├── attribute.utils.test.ts │ │ ├── common.utils.test.tsx │ │ ├── eservice.utils.test.ts │ │ ├── form.utils.test.ts │ │ ├── format.utils.test.ts │ │ ├── purpose.utils.test.ts │ │ ├── risk-analysis-form.utils.test.ts │ │ └── tenant.utils.test.ts │ ├── agreement.utils.ts │ ├── array.utils.ts │ ├── attribute.utils.ts │ ├── common.utils.ts │ ├── errors.utils.ts │ ├── eservice.utils.ts │ ├── form.utils.ts │ ├── format.utils.ts │ ├── purpose.utils.ts │ ├── risk-analysis-form.utils.ts │ ├── tenant.utils.ts │ └── testing.utils.tsx └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | scripts/* 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Segnalaci un bug 4 | title: '' 5 | labels: bug 6 | 7 | --- 8 | 9 | Per favore, compila quello che puoi dei campi seguenti. Ci aiuti a risolvere il problema più rapidamente 10 | 11 | **Descrivi il bug** 12 | Una breve descrizione 13 | 14 | **Step per riprodurre il bug** 15 | 1. Vai su '...' 16 | 2. Clicca su '....' 17 | 3. Vedi l'errore 18 | 19 | **Comportamento atteso** 20 | Cosa ti aspetteresti? 21 | 22 | **Il codice IPA del tuo ente** 23 | Se non lo sai, lo trovi così: 24 | 1. vai [qui](https://www.indicepa.gov.it/ipa-portale/consultazione/indirizzo-sede/ricerca-ente) 25 | 2. inserisci il nome del tuo ente e clicca "Ricerca" 26 | 3. sul risultato che ti interessa, clicca nella casella di "Ulteriori informazioni" 27 | 4. nella nuova pagina che si apre, sarà visibile il codice IPA di fianco al nome dell'ente 28 | 29 | **Data e ora in cui si è riscontrato il problema** 30 | es. 24/11/2021 h. 10:20 31 | 32 | **Ambiente** 33 | Produzione o Collaudo 34 | 35 | **Quali permessi ha la tua utenza?** 36 | es. Amministratore (operatore amministrativo), Operatore API, Operatore di sicurezza 37 | 38 | **Screenshot** 39 | Se possibile, incolla uno screenshot che mostra il punto in cui si presenta il bug 40 | 41 | **Note** 42 | Ulteriori informazioni che possono aiutare per fare debugging 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/service_maintenance.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Service Maintenance 3 | about: Use for trace maintenance activities 4 | title: "Manutenzione programmata ambiente di collaudo/produzione" 5 | labels: 'maintenance' 6 | assignees: '' 7 | 8 | --- 9 | **Se singolo giorno**: [PDND Interoperabilità](https://selfcare.interop.pagopa.it) sarà sottoposto a manutenzione programmata il gg/mm alle hh:mm per circa [X] ore. Non sono previsti impatti sul servizio di stacco dei voucher. Ci scusiamo per gli eventuali disservizi causati. 10 | 11 | **Se più giorni**: [PDND Interoperabilità](https://selfcare.interop.pagopa.it) sarà sottoposto a manutenzione programmata dalle hh:mm del gg/mm/aaaa alle hh:mm del gg/mm/aaaa. Non sono previsti impatti sul servizio di stacco dei voucher. Ci scusiamo per gli eventuali disservizi causati. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/voucher_errors.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Errore voucher 3 | about: Stai cercando di ottenere un voucher e ricevi errore 4 | title: '' 5 | labels: voucher-errors 6 | 7 | --- 8 | 9 | **All'interno del back office di Interoperabilità è possibile fare debug delle tue client assertion in autonomia. Trovi la funzionalità sotto "Fruizione" > "Debug client assertion".** 10 | 11 | Attenzione: la tua client assertion è un'informazione sensibile. Non inserirla all'interno di questa issue. Ogni richiesta contenente informazioni riservate o sensibili sarà rimossa senza preavviso dagli amministratori. 12 | 13 | **Il codice IPA del tuo ente** 14 | Se non lo sai, lo trovi così: 15 | 1. vai [qui](https://www.indicepa.gov.it/ipa-portale/consultazione/indirizzo-sede/ricerca-ente) 16 | 2. inserisci il nome del tuo ente e clicca "Ricerca" 17 | 3. sul risultato che ti interessa, clicca nella casella di "Ulteriori informazioni" 18 | 4. nella nuova pagina che si apre, sarà visibile il codice IPA di fianco al nome dell'ente 19 | 20 | **Data e ora del tentativo** 21 | es. 24/11/2021 h. 10:20 22 | 23 | **Ambiente** 24 | Produzione o Collaudo 25 | 26 | **client_id** 27 | Es. `3b0d583b-7d4c-4bcf-a27c-1d82e81fa321` 28 | 29 | **correlationId** 30 | Il campo che identifica la richiesta, viene restituito dal backend all'interno della risposta assieme all'errore. Es. `47508f4c-bc49-4d34-8adb-a85f734b45b9` 31 | 32 | **purposeId (solo per i client e-service)** 33 | È uno dei campi che hai inserito all'interno della client assertion. Es. `69fb9f60-bd47-4b4d-9652-9747f7953234` 34 | -------------------------------------------------------------------------------- /.github/workflows/sonar-build-spec.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - 1.0.x 6 | pull_request: 7 | types: [opened, synchronize, reopened] 8 | jobs: 9 | sonarcloud: 10 | name: SonarCloud 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 14 | with: 15 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 16 | - name: Install dependencies 17 | run: npm install 18 | - name: Test and coverage 19 | run: npm run coverage 20 | - name: SonarCloud Scan 21 | uses: SonarSource/sonarcloud-github-action@master 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 24 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | scripts/guide-crawl/output/ 25 | 26 | # testing 27 | /coverage 28 | 29 | # production 30 | /build 31 | 32 | # misc 33 | .DS_Store 34 | .env.local 35 | .env.development.local 36 | .env.development 37 | .env.test.local 38 | .env.production.local 39 | 40 | npm-debug.log* 41 | yarn-debug.log* 42 | yarn-error.log* 43 | 44 | # yarn modern (non zero-installs) 45 | .pnp.* 46 | .yarn/* 47 | !.yarn/patches 48 | !.yarn/plugins 49 | !.yarn/releases 50 | !.yarn/sdks 51 | !.yarn/versions 52 | 53 | # rollup-plugin-visualizer output 54 | stats.html -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | node_modules/ 3 | README.md 4 | CODEOWNERS -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": false, 4 | "singleQuote": true, 5 | "trailingComma": "es5" 6 | } 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## 0.1.0 (2021-11-24) 6 | 7 | 8 | ### Chores 9 | 10 | * added automatic formatting and linting before commit ([14c7eda](https://github.com/pagopa/pdnd-interop-frontend/commit/14c7eda21d6261a873d1bf25b53ed63ed02c0a86)) 11 | * added standard version to automate changelog and version tagging ([ae996da](https://github.com/pagopa/pdnd-interop-frontend/commit/ae996da8bf723b55ff6aa96b9e6a55af5f523bf9)) 12 | * added tooling to enforce conventional commits ([d1853da](https://github.com/pagopa/pdnd-interop-frontend/commit/d1853daca652bd00eac5046962a163b38de23533)) 13 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Code owned by the following team members 2 | * @ruggerocastagnola @Carminepo2 @borgesis95 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22.14.0@sha256:cfef4432ab2901fd6ab2cb05b177d3c6f8a7f48cb22ad9d7ae28bb6aa5f8b471 as build 2 | 3 | WORKDIR /app 4 | 5 | COPY . . 6 | RUN npm i 7 | RUN npm run build 8 | 9 | FROM nginx@sha256:67682bda769fae1ccf5183192b8daf37b64cae99c6c3302650f6f8bf5f0f95df 10 | RUN mkdir -p /usr/share/nginx/html/ui 11 | COPY --from=build /app/dist /usr/share/nginx/html/ui 12 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | 3 | agent none 4 | 5 | stages { 6 | stage('Test and Publish') { 7 | agent { label 'sbt-template' } 8 | environment { 9 | DOCKER_REPO = "${env.DOCKER_REPO}" 10 | ECR_RW = credentials('ecr-rw') 11 | VERSION = getVersion() 12 | } 13 | steps { 14 | container('sbt-container') { 15 | script { 16 | sh 'docker build --network host -t $DOCKER_REPO/interop-frontend:$VERSION .' 17 | withCredentials([usernamePassword(credentialsId: 'ecr-rw', usernameVariable: 'AWS_ACCESS_KEY_ID', passwordVariable: 'AWS_SECRET_ACCESS_KEY')]) { 18 | sh ''' 19 | aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin $DOCKER_REPO 20 | ''' 21 | } 22 | sh 'docker image push $DOCKER_REPO/interop-frontend:$VERSION' 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } 29 | 30 | 31 | String getVersion() { 32 | if(env.TAG_NAME) 33 | return env.TAG_NAME 34 | else 35 | return env.GIT_LOCAL_BRANCH + '-latest' 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PDND Interoperabilità: Frontend 2 | Sede della repo frontend del back office di prodotto. Disciplinata nell'articolo 50-ter del [C.A.D.](https://www.normattiva.it/atto/caricaDettaglioAtto?atto.dataPubblicazioneGazzetta=2005-05-16&atto.codiceRedazionale=005G0104&atto.articolo.numero=0&atto.articolo.sottoArticolo=1&atto.articolo.sottoArticolo1=10&qId=5614860b-4769-478e-bf22-a8a76a04159a&tabID=0.5538263478162919&title=lbl.dettaglioAtto), PDND Interoperabilità è la piattaforma che abilita lo scambio di informazioni tra gli enti. Per maggiori informazioni, visita la [landing di prodotto](https://interop.pagopa.it). 3 | 4 | ## Richiedere assistenza 5 | Il canale primario di assistenza per PDND Interoperabilità è disponibile all'interno del back office del prodotto, nel link in alto a destra ("Assistenza"). È tuttavia possibile aprire una issue pubblica su Github per tutte quelle informazioni, chiarimenti o proposte di feature che possono essere di pubblico interesse. 6 | 7 | **NB: è fatto divieto di inserire all'interno delle issue Github pubbliche le proprie client assertion o qualsiasi altro materiale di natura potenzialmente riservata o sensibile. Le issue che contengono informazioni potenzialmente sensibili potranno essere rimosse senza preavviso dagli amministratori.** 8 | -------------------------------------------------------------------------------- /__mocks__/data/client.mocks.ts: -------------------------------------------------------------------------------- 1 | import type { Client } from '@/api/api.generatedTypes' 2 | import { createMockFactory } from '../../src/utils/testing.utils' 3 | 4 | const createMockClient = createMockFactory({ 5 | id: '85ceaa96-a95e-4cf9-b1f9-b85be1e09369', 6 | name: 'test - 24/02/23', 7 | consumer: { 8 | id: 'consumer-id', 9 | name: 'PagoPa', 10 | }, 11 | description: 'test test test', 12 | kind: 'CONSUMER', 13 | purposes: [], 14 | createdAt: '2021-02-24T15:00:00.000Z', 15 | }) 16 | 17 | export { createMockClient } 18 | -------------------------------------------------------------------------------- /__mocks__/data/delegation.mocks.ts: -------------------------------------------------------------------------------- 1 | import { createMockFactory } from '../../src/utils/testing.utils' 2 | import type { DelegationWithCompactTenants } from '../../src/api/api.generatedTypes' 3 | 4 | const createMockDelegationWithCompactTenants = createMockFactory({ 5 | id: 'delegation-id', 6 | delegator: { 7 | id: 'delegator-id', 8 | name: 'delegator-name', 9 | kind: 'PA', 10 | contactMail: { address: 'test-email', description: 'test' }, 11 | }, 12 | delegate: { 13 | id: 'delegate-id', 14 | name: 'delegate-name', 15 | kind: 'PA', 16 | contactMail: { address: 'test-email', description: 'test' }, 17 | }, 18 | }) 19 | 20 | export { createMockDelegationWithCompactTenants } 21 | -------------------------------------------------------------------------------- /__mocks__/data/key.mocks.ts: -------------------------------------------------------------------------------- 1 | import type { PublicKey } from '../../src/api/api.generatedTypes' 2 | import { createMockFactory } from '../../src/utils/testing.utils' 3 | 4 | export const createMockPublicKey = createMockFactory({ 5 | keyId: 'Fxod41P3BZoe2HT6BuEw3SNFlu9ufAkYgpmtBoNuVkg', 6 | name: 'string', 7 | user: { 8 | userId: '3fa85f64-5717-4562-b3fc-2c963f66afa6', 9 | name: 'Mario', 10 | surname: 'Rossi', 11 | }, 12 | createdAt: '2023-02-28T10:57:30.512218Z', 13 | isOrphan: true, 14 | }) 15 | -------------------------------------------------------------------------------- /__mocks__/data/one-trust-notice.mocks.ts: -------------------------------------------------------------------------------- 1 | import type { PrivacyNotice } from './../../src/api/api.generatedTypes' 2 | import { createMockFactory } from '../../src/utils/testing.utils' 3 | 4 | const createMockPrivacyNotice = createMockFactory({ 5 | id: 'id', 6 | userId: 'userId', 7 | consentType: 'PP', 8 | firstAccept: false, 9 | isUpdated: false, 10 | latestVersionId: 'latestVersionId', 11 | }) 12 | 13 | export { createMockPrivacyNotice } 14 | -------------------------------------------------------------------------------- /__mocks__/data/user.mocks.ts: -------------------------------------------------------------------------------- 1 | import type { User } from '@/api/api.generatedTypes' 2 | import type { JwtUser } from '@/types/party.types' 3 | import { createMockFactory } from '@/utils/testing.utils' 4 | 5 | const mockJwtOrg = { 6 | name: 'orgName', 7 | roles: [{ partyRole: 'MANAGER' as const, role: 'admin' as const }], 8 | fiscal_code: 'AAAAAA11A11A111A', 9 | } 10 | 11 | const createMockJwtUser = createMockFactory({ 12 | aud: 'aud', 13 | exp: 1972913491, 14 | iat: 123, 15 | iss: 'iss', 16 | jti: 'jti', 17 | nbf: 123, 18 | organization: mockJwtOrg, 19 | selfcareId: 'selfcareId', 20 | uid: 'uid', 21 | name: 'name', 22 | family_name: 'family_name', 23 | organizationId: 'organizationId', 24 | externalId: { 25 | origin: 'IPA', 26 | value: 'value', 27 | }, 28 | }) 29 | 30 | const createMockSelfCareUser = createMockFactory({ 31 | familyName: 'Rossi', 32 | userId: 'b7f6b32e-6252-4994-ac7b-47622e674e5a', 33 | name: 'Mario', 34 | roles: ['DELEGATE'], 35 | tenantId: '1962d21c-c701-4805-93f6-53a877898756', 36 | }) 37 | 38 | export { createMockJwtUser, createMockSelfCareUser } 39 | -------------------------------------------------------------------------------- /__mocks__/react-i18next.ts: -------------------------------------------------------------------------------- 1 | import noop from 'lodash/noop' 2 | 3 | export const useTranslation = () => { 4 | return { 5 | t: (str: string) => str, 6 | i18n: { 7 | changeLanguage: () => new Promise(noop), 8 | language: 'it', 9 | }, 10 | } 11 | } 12 | 13 | export const Trans = ({ children }) => children 14 | -------------------------------------------------------------------------------- /__mocks__/zustand.ts: -------------------------------------------------------------------------------- 1 | import { create as actualCreate, StateCreator } from 'zustand' 2 | import { act } from 'react-dom/test-utils' 3 | 4 | // a variable to hold reset functions for all stores declared in the app 5 | const storeResetFns = new Set<() => void>() 6 | 7 | // when creating a store, we get its initial state, create a reset function and add it in the set 8 | export const create = (createState: StateCreator) => { 9 | const store = actualCreate(createState) 10 | const initialState = store.getState() 11 | storeResetFns.add(() => store.setState(initialState, true)) 12 | 13 | return store 14 | } 15 | 16 | // Reset all stores after each test run 17 | beforeEach(() => { 18 | act(() => storeResetFns.forEach((resetFn) => resetFn())) 19 | }) 20 | -------------------------------------------------------------------------------- /public/data/it/public-key.json: -------------------------------------------------------------------------------- 1 | {"html":"

Come generare le chiavi

\n

Apri il terminale e incolla i comandi qui sotto. Per cambiare nome alla chiave, sostituisci "client-test-keypair" con il filename che vuoi dare alla chiave.

\n
openssl genrsa -out client-test-keypair.rsa.pem 2048\nopenssl rsa -in client-test-keypair.rsa.pem -pubout -out client-test-keypair.rsa.pub\nopenssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in client-test-keypair.rsa.pem -out client-test-keypair.rsa.priv\n
\n

Come caricare le chiavi

\n
    \n
  1. Dopo aver generato la coppia di chiavi e averle riposte al sicuro, copia l’intero contenuto del file della chiave pubblica (quella che finisce in .pub); assicurati di includere anche le parti iniziale e finale (inizia con -----BEGIN PUBLIC KEY----- e finisce con -----END PUBLIC KEY-----);
  2. \n
  3. torna sulla piattaforma;
  4. \n
  5. all’interno della tab "Chiavi pubbliche" nel client di interesse, troverai un bottone "+ Aggiungi";
  6. \n
  7. a quel punto, clicca su "Carica". Riceverai immediatamente riscontro se il caricamento sia andato a buon fine o meno. Se dovessero verificarsi errori, segui le istruzioni indicate nel messaggio di "feedback".
  8. \n
\n"} -------------------------------------------------------------------------------- /public/data/it/session_token_curl.txt: -------------------------------------------------------------------------------- 1 | curl --location --request POST AUTHORIZATION_SERVER_TOKEN_CREATION_URL \ 2 | --header 'Content-Type: application/x-www-form-urlencoded' \ 3 | --data-urlencode 'client_id=CLIENT_ID' \ 4 | --data-urlencode 'client_assertion=LA_TUA_CLIENT_ASSERTION' \ 5 | --data-urlencode 'client_assertion_type=CLIENT_ASSERTION_TYPE' \ 6 | --data-urlencode 'grant_type=GRANT_TYPE' -------------------------------------------------------------------------------- /public/data/it/voucher-python-invoke.txt: -------------------------------------------------------------------------------- 1 | python create_client_assertion.py \ 2 | --kid=INSERISCI_VALORE_KID \ 3 | --alg=INSERISCI_VALORE_ALG \ 4 | --typ=INSERISCI_VALORE_TYP \ 5 | --issuer=INSERISCI_VALORE_ISS \ 6 | --subject=INSERISCI_VALORE_SUB \ 7 | --audience=INSERISCI_VALORE_AUD \ 8 | --purposeId=INSERISCI_VALORE_PUR \ 9 | --keyPath=PATH_CHIAVE_PRIVATA -------------------------------------------------------------------------------- /public/data/it/voucher-python-m2m-invoke.txt: -------------------------------------------------------------------------------- 1 | python create_m2m_client_assertion.py \ 2 | --kid=INSERISCI_VALORE_KID \ 3 | --alg=INSERISCI_VALORE_ALG \ 4 | --typ=INSERISCI_VALORE_TYP \ 5 | --issuer=INSERISCI_VALORE_ISS \ 6 | --subject=INSERISCI_VALORE_SUB \ 7 | --audience=INSERISCI_VALORE_AUD \ 8 | --keyPath=PATH_CHIAVE_PRIVATA -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/icons/icon-144x144.png -------------------------------------------------------------------------------- /public/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/icons/icon-192x192.png -------------------------------------------------------------------------------- /public/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/icons/icon-256x256.png -------------------------------------------------------------------------------- /public/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/icons/icon-384x384.png -------------------------------------------------------------------------------- /public/icons/icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/icons/icon-48x48.png -------------------------------------------------------------------------------- /public/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/icons/icon-512x512.png -------------------------------------------------------------------------------- /public/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/icons/icon-72x72.png -------------------------------------------------------------------------------- /public/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/public/icons/icon-96x96.png -------------------------------------------------------------------------------- /public/manifest.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pagopa interop", 3 | "short_name": "pagopa interop", 4 | "start_url": "/", 5 | "background_color": "#FFFFFF", 6 | "theme_color": "#00a1b0", 7 | "display": "minimal-ui", 8 | "icons": [ 9 | { 10 | "src": "icons/icon-48x48.png", 11 | "sizes": "48x48", 12 | "type": "image/png" 13 | }, 14 | { 15 | "src": "icons/icon-72x72.png", 16 | "sizes": "72x72", 17 | "type": "image/png" 18 | }, 19 | { 20 | "src": "icons/icon-96x96.png", 21 | "sizes": "96x96", 22 | "type": "image/png" 23 | }, 24 | { 25 | "src": "icons/icon-144x144.png", 26 | "sizes": "144x144", 27 | "type": "image/png" 28 | }, 29 | { 30 | "src": "icons/icon-192x192.png", 31 | "sizes": "192x192", 32 | "type": "image/png" 33 | }, 34 | { 35 | "src": "icons/icon-256x256.png", 36 | "sizes": "256x256", 37 | "type": "image/png" 38 | }, 39 | { 40 | "src": "icons/icon-384x384.png", 41 | "sizes": "384x384", 42 | "type": "image/png" 43 | }, 44 | { 45 | "src": "icons/icon-512x512.png", 46 | "sizes": "512x512", 47 | "type": "image/png" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: / 4 | 5 | User-agent: AdsBot-Google 6 | Disallow: / -------------------------------------------------------------------------------- /scripts/help/data/it/public-key.md: -------------------------------------------------------------------------------- 1 | ## Come generare le chiavi 2 | 3 | Apri il terminale e incolla i comandi qui sotto. Per cambiare nome alla chiave, sostituisci "client-test-keypair" con il filename che vuoi dare alla chiave. 4 | 5 | ``` 6 | openssl genrsa -out client-test-keypair.rsa.pem 2048 7 | openssl rsa -in client-test-keypair.rsa.pem -pubout -out client-test-keypair.rsa.pub 8 | openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in client-test-keypair.rsa.pem -out client-test-keypair.rsa.priv 9 | ``` 10 | 11 | ## Come caricare le chiavi 12 | 13 | 1. Dopo aver generato la coppia di chiavi e averle riposte al sicuro, copia l’intero contenuto del file della chiave pubblica (quella che finisce in .pub); assicurati di includere anche le parti iniziale e finale (inizia con `-----BEGIN PUBLIC KEY-----` e finisce con `-----END PUBLIC KEY-----`); 14 | 2. torna sulla piattaforma; 15 | 3. all’interno della tab "Chiavi pubbliche" nel client di interesse, troverai un bottone "+ Aggiungi"; 16 | 4. a quel punto, clicca su "Carica". Riceverai immediatamente riscontro se il caricamento sia andato a buon fine o meno. Se dovessero verificarsi errori, segui le istruzioni indicate nel messaggio di "feedback". 17 | -------------------------------------------------------------------------------- /scripts/help/markdown-to-html.js: -------------------------------------------------------------------------------- 1 | import { readdirSync, readFileSync, writeFileSync } from 'fs' 2 | import { join, resolve } from 'path' 3 | import { marked } from 'marked' 4 | 5 | const FOLDERS = [{ markdownLocation: './scripts/help/data/it/', destLocation: './public/data/it/' }] 6 | 7 | FOLDERS.forEach(({ markdownLocation, destLocation }) => { 8 | const markdownFolderPath = join(resolve(), markdownLocation) 9 | const files = readdirSync(markdownFolderPath) 10 | 11 | files.forEach((inputFilename) => { 12 | try { 13 | const markdownPath = join(markdownFolderPath, inputFilename) 14 | const markdownData = readFileSync(markdownPath, 'utf8') 15 | const html = marked.parse(markdownData) 16 | const outputFilename = inputFilename.replace('.md', '.json') 17 | const outputPath = join(resolve(), destLocation, outputFilename) 18 | writeFileSync(outputPath, JSON.stringify({ html }), { flag: 'w' }) 19 | } catch (err) { 20 | console.error(err) 21 | } 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /scripts/open-api-type-generator.js: -------------------------------------------------------------------------------- 1 | import { generateApi } from 'swagger-typescript-api' 2 | import path from 'path' 3 | 4 | const openApiSpecificationFileUrl = 5 | 'https://raw.githubusercontent.com/pagopa/interop-be-monorepo/refs/heads/develop/packages/api-clients/open-api/bffApi.yml' 6 | 7 | const apiFolderPath = path.resolve('./src/api/') 8 | 9 | generateApi({ 10 | name: 'api.generatedTypes.ts', 11 | url: openApiSpecificationFileUrl, 12 | output: apiFolderPath, 13 | generateClient: false, 14 | generateUnionEnums: true, 15 | extractRequestParams: true, 16 | extractRequestBody: true, 17 | generateRouteTypes: true, 18 | }).catch((e) => console.error(e)) 19 | -------------------------------------------------------------------------------- /setupTests.ts: -------------------------------------------------------------------------------- 1 | import { afterEach, vi } from 'vitest' 2 | import { cleanup } from '@testing-library/react' 3 | // extends Vitest's expect method with methods from react-testing-library 4 | import '@testing-library/jest-dom/vitest' 5 | 6 | // runs a cleanup after each test case (e.g. clearing jsdom) 7 | afterEach(() => { 8 | cleanup() 9 | }) 10 | 11 | // vi.spyOn(global.console, 'log').mockImplementation(() => vi.fn()) 12 | vi.spyOn(global.console, 'error').mockImplementation(() => vi.fn()) 13 | vi.spyOn(global.console, 'warn').mockImplementation(() => vi.fn()) 14 | 15 | global.crypto.randomUUID = () => 16 | Math.random().toString() as `${string}-${string}-${string}-${string}-${string}` 17 | 18 | vi.stubGlobal('scroll', vi.fn()) 19 | vi.mock('zustand') 20 | vi.mock('react-i18next') 21 | 22 | process.env = { 23 | ...process.env, 24 | // This is needed in order to make the tests work. 25 | // The chosen REACT_APP_API_HOST value is random and could be any string. 26 | REACT_APP_API_HOST: 'http://localhost:8080', 27 | } 28 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.organization=pagopa 2 | sonar.projectKey=pagopa_pdnd-interop-frontend 3 | sonar.sources=src/ 4 | sonar.exclusions=src/**/__tests__/**/*,src/**/__test__/** 5 | sonar.tests=src/ 6 | sonar.test.inclusions=src/**/__tests__/**/*,src/**/__test__/** 7 | sonar.cpd.exclusions=src/**/__tests__/**,src/**/__test__/**,__mocks__/** 8 | sonar.javascript.lcov.reportPaths=coverage/lcov.info 9 | -------------------------------------------------------------------------------- /src/api/agreement/agreement.downloads.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { useDownloadFile } from '../hooks' 3 | import { AgreementServices } from './agreement.services' 4 | 5 | function useDownloadDocument() { 6 | const { t } = useTranslation('mutations-feedback', { 7 | keyPrefix: 'agreement.downloadDraftDocument', 8 | }) 9 | return useDownloadFile(AgreementServices.downloadDraftDocument, { 10 | errorToastLabel: t('outcome.error'), 11 | loadingLabel: t('loading'), 12 | }) 13 | } 14 | 15 | function useDownloadContract() { 16 | const { t } = useTranslation('mutations-feedback', { 17 | keyPrefix: 'agreement.downloadContract', 18 | }) 19 | return useDownloadFile(AgreementServices.downloadContract, { 20 | errorToastLabel: t('outcome.error'), 21 | loadingLabel: t('loading'), 22 | }) 23 | } 24 | 25 | export const AgreementDownloads = { 26 | useDownloadDocument, 27 | useDownloadContract, 28 | } 29 | -------------------------------------------------------------------------------- /src/api/agreement/index.ts: -------------------------------------------------------------------------------- 1 | export * from './agreement.mutations' 2 | export * from './agreement.queries' 3 | export * from './agreement.downloads' 4 | export * from './agreement.services' 5 | -------------------------------------------------------------------------------- /src/api/api.types.ts: -------------------------------------------------------------------------------- 1 | export type PaginationParams = { 2 | limit: number 3 | offset: number 4 | } 5 | -------------------------------------------------------------------------------- /src/api/attribute/index.ts: -------------------------------------------------------------------------------- 1 | export * from './attribute.queries' 2 | export * from './attribute.mutations' 3 | export * from './attribute.services' 4 | -------------------------------------------------------------------------------- /src/api/auth/auth.hooks.ts: -------------------------------------------------------------------------------- 1 | import { useSuspenseQuery } from '@tanstack/react-query' 2 | import { AuthQueries } from './auth.queries' 3 | import { parseJwt } from './auth.utils' 4 | 5 | function useJwt() { 6 | const { data: sessionToken } = useSuspenseQuery(AuthQueries.getSessionToken()) 7 | return parseJwt(sessionToken) 8 | } 9 | 10 | export const AuthHooks = { 11 | useJwt, 12 | } 13 | -------------------------------------------------------------------------------- /src/api/auth/auth.mutations.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { AuthServices } from './auth.services' 3 | import { useMutation } from '@tanstack/react-query' 4 | 5 | function useSwapSAMLTokens() { 6 | const { t } = useTranslation('mutations-feedback', { keyPrefix: 'auth' }) 7 | return useMutation({ 8 | mutationFn: AuthServices.swapSAMLToken, 9 | meta: { 10 | loadingLabel: t('loadingLabel'), 11 | }, 12 | }) 13 | } 14 | 15 | export const AuthMutations = { 16 | useSwapSAMLTokens, 17 | } 18 | -------------------------------------------------------------------------------- /src/api/auth/auth.queries.ts: -------------------------------------------------------------------------------- 1 | import { queryOptions } from '@tanstack/react-query' 2 | import { AuthServices } from './auth.services' 3 | import { STAGE } from '@/config/env' 4 | 5 | function getSessionToken() { 6 | return queryOptions({ 7 | queryKey: ['AuthGetSessionToken'], 8 | queryFn: AuthServices.getSessionToken, 9 | staleTime: Infinity, 10 | gcTime: Infinity, 11 | retry: false, 12 | }) 13 | } 14 | 15 | function getBlacklist() { 16 | return queryOptions({ 17 | queryKey: ['AuthGetBlacklist'], 18 | queryFn: AuthServices.getBlacklist, 19 | enabled: STAGE === 'PROD', 20 | throwOnError: false, 21 | staleTime: Infinity, 22 | gcTime: Infinity, 23 | retry: false, 24 | }) 25 | } 26 | 27 | export const AuthQueries = { 28 | getSessionToken, 29 | getBlacklist, 30 | } 31 | -------------------------------------------------------------------------------- /src/api/auth/auth.utils.ts: -------------------------------------------------------------------------------- 1 | import { PRODUCER_ALLOWED_ORIGINS } from '@/config/env' 2 | import type { JwtUser } from '@/types/party.types' 3 | import memoize from 'lodash/memoize' 4 | import { jwtDecode } from 'jwt-decode' 5 | 6 | export type ParsedJwt = ReturnType 7 | 8 | /** 9 | * Parse the JWT token and return the user informations stored in it 10 | */ 11 | export const parseJwt = memoize((token: string | null | undefined) => { 12 | const jwt = token ? jwtDecode(token) : undefined 13 | const currentRoles = jwt ? jwt.organization.roles.map((r) => r.role) : [] 14 | const isAdmin = currentRoles.length === 1 && currentRoles[0] === 'admin' 15 | const isOperatorAPI = currentRoles.includes('api') 16 | const isOperatorSecurity = currentRoles.includes('security') 17 | const isSupport = currentRoles.includes('support') 18 | const isOrganizationAllowedToProduce = !!( 19 | jwt?.externalId && PRODUCER_ALLOWED_ORIGINS.includes(jwt.externalId.origin) 20 | ) 21 | 22 | return { 23 | jwt, 24 | currentRoles, 25 | isAdmin, 26 | isOperatorAPI, 27 | isOperatorSecurity, 28 | isSupport, 29 | isOrganizationAllowedToProduce, 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /src/api/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './auth.queries' 2 | export * from './auth.mutations' 3 | export * from './auth.services' 4 | export * from './auth.hooks' 5 | -------------------------------------------------------------------------------- /src/api/client/client.downloads.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { useDownloadFile } from '../hooks' 3 | import { ClientServices } from './client.services' 4 | 5 | function useDownloadKey() { 6 | const { t } = useTranslation('mutations-feedback', { keyPrefix: 'client.downloadKey' }) 7 | return useDownloadFile(ClientServices.downloadKey, { 8 | loadingLabel: t('loading'), 9 | }) 10 | } 11 | 12 | export const ClientDownloads = { 13 | useDownloadKey, 14 | } 15 | -------------------------------------------------------------------------------- /src/api/client/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client.queries' 2 | export * from './client.mutations' 3 | export * from './client.services' 4 | export * from './client.downloads' 5 | -------------------------------------------------------------------------------- /src/api/delegation/delegation.downloads.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { useDownloadFile } from '../hooks' 3 | import { DelegationServices } from './delegation.services' 4 | 5 | function useDownloadDelegationContract() { 6 | const { t } = useTranslation('mutations-feedback', { 7 | keyPrefix: 'delegation.downloadDelegationContract', 8 | }) 9 | return useDownloadFile(DelegationServices.downloadDelegationContract, { 10 | errorToastLabel: t('outcome.error'), 11 | loadingLabel: t('loading'), 12 | }) 13 | } 14 | 15 | export const DelegationDownloads = { 16 | useDownloadDelegationContract, 17 | } 18 | -------------------------------------------------------------------------------- /src/api/delegation/index.ts: -------------------------------------------------------------------------------- 1 | export * from './delegation.mutations' 2 | export * from './delegation.queries' 3 | export * from './delegation.services' 4 | export * from './delegation.downloads' 5 | -------------------------------------------------------------------------------- /src/api/eservice/eservice.downloads.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { useDownloadFile } from '../hooks' 3 | import { EServiceServices } from './eservice.services' 4 | 5 | function useDownloadVersionDocument() { 6 | const { t } = useTranslation('mutations-feedback', { 7 | keyPrefix: 'eservice.downloadVersionDraftDocument', 8 | }) 9 | return useDownloadFile(EServiceServices.downloadVersionDraftDocument, { 10 | errorToastLabel: t('outcome.error'), 11 | loadingLabel: t('loading'), 12 | }) 13 | } 14 | 15 | function useDownloadConsumerList() { 16 | const { t } = useTranslation('mutations-feedback', { 17 | keyPrefix: 'eservice.downloadConsumerList', 18 | }) 19 | return useDownloadFile(EServiceServices.downloadConsumerList, { 20 | errorToastLabel: t('outcome.error'), 21 | successToastLabel: t('outcome.success'), 22 | loadingLabel: t('loading'), 23 | }) 24 | } 25 | 26 | function useExportVersion() { 27 | const { t } = useTranslation('mutations-feedback', { 28 | keyPrefix: 'eservice.exportVersion', 29 | }) 30 | return useDownloadFile(EServiceServices.exportVersion, { 31 | errorToastLabel: t('outcome.error'), 32 | loadingLabel: t('loading'), 33 | }) 34 | } 35 | 36 | export const EServiceDownloads = { 37 | useDownloadVersionDocument, 38 | useDownloadConsumerList, 39 | useExportVersion, 40 | } 41 | -------------------------------------------------------------------------------- /src/api/eservice/index.ts: -------------------------------------------------------------------------------- 1 | export * from './eservice.queries' 2 | export * from './eservice.mutations' 3 | export * from './eservice.downloads' 4 | export * from './eservice.services' 5 | -------------------------------------------------------------------------------- /src/api/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useDownloadFile' 2 | -------------------------------------------------------------------------------- /src/api/keychain/index.ts: -------------------------------------------------------------------------------- 1 | export * from './keychain.queries' 2 | export * from './keychain.mutations' 3 | export * from './keychain.services' 4 | export * from './keychain.downloads' 5 | -------------------------------------------------------------------------------- /src/api/keychain/keychain.downloads.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { useDownloadFile } from '../hooks' 3 | import { KeychainServices } from './keychain.services' 4 | 5 | function useDownloadKey() { 6 | const { t } = useTranslation('mutations-feedback', { keyPrefix: 'keychain.downloadKey' }) 7 | return useDownloadFile(KeychainServices.downloadKey, { 8 | loadingLabel: t('loading'), 9 | }) 10 | } 11 | 12 | export const KeychainDownloads = { 13 | useDownloadKey, 14 | } 15 | -------------------------------------------------------------------------------- /src/api/maintenance/index.ts: -------------------------------------------------------------------------------- 1 | export * from './maintenance.queries' 2 | export * from './maintenance.services' 3 | -------------------------------------------------------------------------------- /src/api/maintenance/maintenance.queries.ts: -------------------------------------------------------------------------------- 1 | import { queryOptions } from '@tanstack/react-query' 2 | import { MaintenanceServices } from './maintenance.services' 3 | 4 | function getMaintenanceJson() { 5 | return queryOptions({ 6 | queryKey: ['GetMaintenanceJson'], 7 | queryFn: MaintenanceServices.getMaintenanceJson, 8 | throwOnError: false, 9 | retry: false, 10 | staleTime: Infinity, 11 | gcTime: Infinity, 12 | }) 13 | } 14 | 15 | export const MaintenanceQueries = { 16 | getMaintenanceJson, 17 | } 18 | -------------------------------------------------------------------------------- /src/api/maintenance/maintenance.services.ts: -------------------------------------------------------------------------------- 1 | import { INTEROP_RESOURCES_BASE_URL } from '@/config/env' 2 | import type { MaintenanceData } from '@/hooks/useMaintenanceBanner' 3 | import axios from 'axios' 4 | 5 | async function getMaintenanceJson() { 6 | const response = await axios.get( 7 | `${INTEROP_RESOURCES_BASE_URL}/maintenance-window/data.json` 8 | ) 9 | return response.data 10 | } 11 | 12 | export const MaintenanceServices = { 13 | getMaintenanceJson, 14 | } 15 | -------------------------------------------------------------------------------- /src/api/one-trust-notices/index.ts: -------------------------------------------------------------------------------- 1 | export * from './one-trust-notices.queries' 2 | export * from './one-trust-notices.mutations' 3 | export * from './one-trust-notices.services' 4 | -------------------------------------------------------------------------------- /src/api/one-trust-notices/one-trust-notices.mutations.ts: -------------------------------------------------------------------------------- 1 | import { useMutation } from '@tanstack/react-query' 2 | import { OneTrustNoticesServices } from './one-trust-notices.services' 3 | 4 | function useAcceptPrivacyNotice() { 5 | return useMutation({ 6 | mutationFn: OneTrustNoticesServices.acceptPrivacyNotice, 7 | }) 8 | } 9 | 10 | export const OneTrustNoticesMutations = { 11 | useAcceptPrivacyNotice, 12 | } 13 | -------------------------------------------------------------------------------- /src/api/purpose/index.ts: -------------------------------------------------------------------------------- 1 | export * from './purpose.queries' 2 | export * from './purpose.downloads' 3 | export * from './purpose.mutations' 4 | export * from './purpose.services' 5 | -------------------------------------------------------------------------------- /src/api/purpose/purpose.downloads.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { useDownloadFile } from '../hooks' 3 | import { PurposeServices } from './purpose.services' 4 | 5 | function useDownloadRiskAnalysis() { 6 | const { t } = useTranslation('mutations-feedback', { keyPrefix: 'purpose.downloadRiskAnalysis' }) 7 | return useDownloadFile(PurposeServices.downloadRiskAnalysis, { 8 | loadingLabel: t('loading'), 9 | errorToastLabel: t('outcome.error'), 10 | }) 11 | } 12 | 13 | export const PurposeDownloads = { 14 | useDownloadRiskAnalysis, 15 | } 16 | -------------------------------------------------------------------------------- /src/api/selfcare/index.ts: -------------------------------------------------------------------------------- 1 | export * from './selfcare.queries' 2 | export * from './selfcare.services' 3 | -------------------------------------------------------------------------------- /src/api/selfcare/selfcare.queries.ts: -------------------------------------------------------------------------------- 1 | import { queryOptions } from '@tanstack/react-query' 2 | import { SelfcareServices } from './selfcare.services' 3 | 4 | function getProducts() { 5 | return queryOptions({ 6 | queryKey: ['SelfcareGetProducts'], 7 | queryFn: () => SelfcareServices.getProducts(), 8 | throwOnError: false, 9 | retry: false, 10 | staleTime: Infinity, 11 | gcTime: Infinity, 12 | }) 13 | } 14 | 15 | function getPartyList() { 16 | return queryOptions({ 17 | queryKey: ['SelfcareGetPartyList'], 18 | queryFn: () => SelfcareServices.getPartyList(), 19 | throwOnError: false, 20 | retry: false, 21 | staleTime: Infinity, 22 | gcTime: Infinity, 23 | }) 24 | } 25 | 26 | function getSingleUser(userId: string) { 27 | return queryOptions({ 28 | queryKey: ['SelfcareGetSingleUser', userId], 29 | queryFn: () => SelfcareServices.getSingleUser(userId), 30 | throwOnError: false, 31 | retry: false, 32 | staleTime: Infinity, 33 | gcTime: Infinity, 34 | }) 35 | } 36 | 37 | export const SelfcareQueries = { 38 | getProducts, 39 | getPartyList, 40 | getSingleUser, 41 | } 42 | -------------------------------------------------------------------------------- /src/api/selfcare/selfcare.services.ts: -------------------------------------------------------------------------------- 1 | import { BACKEND_FOR_FRONTEND_URL } from '@/config/env' 2 | import axiosInstance from '@/config/axios' 3 | import type { SelfcareInstitution, User } from '../api.generatedTypes' 4 | 5 | async function getProducts() { 6 | const response = await axiosInstance.get>( 7 | `${BACKEND_FOR_FRONTEND_URL}/selfcare/institutions/products` 8 | ) 9 | return response.data 10 | } 11 | 12 | async function getPartyList() { 13 | const response = await axiosInstance.get>( 14 | `${BACKEND_FOR_FRONTEND_URL}/selfcare/institutions` 15 | ) 16 | return response.data 17 | } 18 | 19 | async function getSingleUser(userId: string) { 20 | const response = await axiosInstance.get(`${BACKEND_FOR_FRONTEND_URL}/users/${userId}`) 21 | return response.data 22 | } 23 | 24 | export const SelfcareServices = { 25 | getProducts, 26 | getPartyList, 27 | getSingleUser, 28 | } 29 | -------------------------------------------------------------------------------- /src/api/template/index.ts: -------------------------------------------------------------------------------- 1 | export * from './template.queries' 2 | export * from './template.mutations' 3 | export * from './template.services' 4 | -------------------------------------------------------------------------------- /src/api/template/template.downloads.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { TemplateServices } from './template.services' 3 | import { useDownloadFile } from '../hooks' 4 | 5 | function useDownloadVersionDocument() { 6 | const { t } = useTranslation('mutations-feedback', { 7 | keyPrefix: 'eservice.downloadVersionDraftDocument', 8 | }) 9 | return useDownloadFile(TemplateServices.downloadVersionDraftDocument, { 10 | errorToastLabel: t('outcome.error'), 11 | loadingLabel: t('loading'), 12 | }) 13 | } 14 | 15 | function useDownloadTemplateConsumerList() { 16 | const { t } = useTranslation('mutations-feedback', { 17 | keyPrefix: 'eservice.downloadConsumerList', 18 | }) 19 | return useDownloadFile(TemplateServices.downloadConsumerList, { 20 | errorToastLabel: t('outcome.error'), 21 | successToastLabel: t('outcome.success'), 22 | loadingLabel: t('loading'), 23 | }) 24 | } 25 | 26 | export const TemplateDownloads = { 27 | useDownloadVersionDocument, 28 | useDownloadTemplateConsumerList, 29 | } 30 | -------------------------------------------------------------------------------- /src/api/tenant/index.ts: -------------------------------------------------------------------------------- 1 | export * from './tenant.queries' 2 | export * from './tenant.mutations' 3 | export * from './tenant.services' 4 | export * from './tenant.hooks' 5 | -------------------------------------------------------------------------------- /src/api/tenant/tenant.hooks.ts: -------------------------------------------------------------------------------- 1 | import { useSuspenseQuery } from '@tanstack/react-query' 2 | import { AuthHooks } from '../auth' 3 | import { TenantQueries } from './tenant.queries' 4 | 5 | function useGetActiveUserParty() { 6 | const { jwt } = AuthHooks.useJwt() 7 | return useSuspenseQuery(TenantQueries.getParty(jwt?.organizationId as string)) 8 | } 9 | 10 | export const TenantHooks = { 11 | useGetActiveUserParty, 12 | } 13 | -------------------------------------------------------------------------------- /src/api/tenant/tenant.mutations.ts: -------------------------------------------------------------------------------- 1 | import { useMutation } from '@tanstack/react-query' 2 | import { useTranslation } from 'react-i18next' 3 | import { TenantServices } from './tenant.services' 4 | 5 | function useUpdateMail() { 6 | const { t } = useTranslation('mutations-feedback', { keyPrefix: 'party.updateMail' }) 7 | return useMutation({ 8 | mutationFn: TenantServices.updateMail, 9 | meta: { 10 | successToastLabel: t('outcome.success'), 11 | errorToastLabel: t('outcome.error'), 12 | loadingLabel: t('loading'), 13 | }, 14 | }) 15 | } 16 | 17 | function useUpdateTenantDelegatedFeatures() { 18 | const { t } = useTranslation('mutations-feedback', { 19 | keyPrefix: 'party.updateDelegationAvailability', 20 | }) 21 | return useMutation({ 22 | mutationFn: TenantServices.UpdateTenantDelegatedFeatures, 23 | meta: { 24 | successToastLabel: t('outcome.success'), 25 | errorToastLabel: t('outcome.error'), 26 | loadingLabel: t('loading'), 27 | }, 28 | }) 29 | } 30 | 31 | export const TenantMutations = { 32 | useUpdateMail, 33 | useUpdateTenantDelegatedFeatures, 34 | } 35 | -------------------------------------------------------------------------------- /src/api/tenant/tenant.queries.ts: -------------------------------------------------------------------------------- 1 | import { queryOptions } from '@tanstack/react-query' 2 | import type { GetInstitutionUsersParams, GetTenantsParams } from '../api.generatedTypes' 3 | import { TenantServices } from './tenant.services' 4 | 5 | function getParty(partyId: string) { 6 | return queryOptions({ 7 | queryKey: ['PartyGetSingle', partyId], 8 | queryFn: () => TenantServices.getParty(partyId), 9 | }) 10 | } 11 | 12 | function getPartyUsersList(params: GetInstitutionUsersParams) { 13 | return queryOptions({ 14 | queryKey: ['PartyGetPartyUsersList', params], 15 | queryFn: () => TenantServices.getPartyUsersList(params), 16 | }) 17 | } 18 | 19 | function getTenants(params: GetTenantsParams) { 20 | return queryOptions({ 21 | queryKey: ['PartyGetTenants', params], 22 | queryFn: () => TenantServices.getTenants(params), 23 | }) 24 | } 25 | 26 | function getHasTenantCertifiedAttributes({ 27 | tenantId, 28 | eserviceId, 29 | descriptorId, 30 | }: { 31 | tenantId: string 32 | eserviceId: string 33 | descriptorId: string 34 | }) { 35 | return queryOptions({ 36 | queryKey: ['PartyGetHasTenantCertifiedAttributes', { tenantId, eserviceId, descriptorId }], 37 | queryFn: () => 38 | TenantServices.verifyTenantCertifiedAttributes({ tenantId, eserviceId, descriptorId }), 39 | }) 40 | } 41 | 42 | export const TenantQueries = { 43 | getParty, 44 | getTenants, 45 | getPartyUsersList, 46 | getHasTenantCertifiedAttributes, 47 | } 48 | -------------------------------------------------------------------------------- /src/api/voucher/index.ts: -------------------------------------------------------------------------------- 1 | export * from './voucher.mutations' 2 | export * from './voucher.services' 3 | -------------------------------------------------------------------------------- /src/api/voucher/voucher.mutations.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { VoucherServices } from './voucher.services' 3 | import { useMutation } from '@tanstack/react-query' 4 | 5 | function useValidateTokenGeneration() { 6 | const { t } = useTranslation('mutations-feedback', { keyPrefix: 'voucher.tokenVerification' }) 7 | return useMutation({ 8 | mutationFn: VoucherServices.validateTokenGeneration, 9 | meta: { 10 | errorToastLabel: t('outcome.error'), 11 | loadingLabel: t('loading'), 12 | }, 13 | }) 14 | } 15 | 16 | export const VoucherMutations = { 17 | useValidateTokenGeneration, 18 | } 19 | -------------------------------------------------------------------------------- /src/api/voucher/voucher.services.ts: -------------------------------------------------------------------------------- 1 | import axiosInstance from '@/config/axios' 2 | import { BACKEND_FOR_FRONTEND_URL } from '@/config/env' 3 | import type { AccessTokenRequest, TokenGenerationValidationResult } from '../api.generatedTypes' 4 | 5 | async function validateTokenGeneration(payload: AccessTokenRequest) { 6 | const response = await axiosInstance.post( 7 | `${BACKEND_FOR_FRONTEND_URL}/tools/validateTokenGeneration`, 8 | payload, 9 | { 10 | headers: { 11 | 'Content-Type': 'application/x-www-form-urlencoded', 12 | }, 13 | } 14 | ) 15 | 16 | return response.data 17 | } 18 | 19 | export const VoucherServices = { 20 | validateTokenGeneration, 21 | } 22 | -------------------------------------------------------------------------------- /src/components/dialogs/DialogError.tsx: -------------------------------------------------------------------------------- 1 | import useResolveError from '@/hooks/useResolveError' 2 | import { Dialog, DialogContent, DialogTitle, Typography } from '@mui/material' 3 | import React from 'react' 4 | import type { FallbackProps } from 'react-error-boundary' 5 | import { useDialog } from '@/stores' 6 | 7 | export const DialogError: React.FC = (fallbackProps) => { 8 | const ariaLabelId = React.useId() 9 | const ariaDescriptionId = React.useId() 10 | 11 | const { title, description, content } = useResolveError(fallbackProps) 12 | const { closeDialog } = useDialog() 13 | 14 | return ( 15 | 22 | {title} 23 | 24 | 25 | {description} 26 | 27 | {content} 28 | 29 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /src/components/dialogs/DialogSessionExpired.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material' 3 | import { useTranslation } from 'react-i18next' 4 | import type { DialogSessionExpiredProps } from '@/types/dialog.types' 5 | import { useNavigate } from '@/router' 6 | 7 | export const DialogSessionExpired: React.FC = () => { 8 | const ariaLabelId = React.useId() 9 | const ariaDescriptionId = React.useId() 10 | 11 | const { t } = useTranslation('shared-components', { keyPrefix: 'dialogSessionExpired' }) 12 | const navigate = useNavigate() 13 | 14 | const logout = React.useCallback(() => { 15 | navigate('LOGOUT') 16 | }, [navigate]) 17 | 18 | React.useEffect(() => { 19 | const timer = setTimeout(logout, 2500) 20 | return () => clearTimeout(timer) 21 | }, [logout]) 22 | 23 | return ( 24 | 25 | {t('title')} 26 | 27 | {t('content.description')} 28 | 29 | 30 | 33 | 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/components/dialogs/__tests__/DialogSessionExpired.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { DialogSessionExpired } from '../DialogSessionExpired' 3 | import { render } from '@testing-library/react' 4 | import { vi } from 'vitest' 5 | import * as router from '@/router' 6 | import userEvent from '@testing-library/user-event' 7 | 8 | const mockFn = vi.fn() 9 | 10 | vi.spyOn(router, 'useNavigate').mockReturnValue(mockFn) 11 | 12 | afterEach(() => { 13 | mockFn.mockClear() 14 | }) 15 | 16 | describe('DialogSessionExpired testing', () => { 17 | it('should call navigate when click button', async () => { 18 | const screen = render() 19 | const button = screen.getByRole('button', { name: 'actions.exitLabel' }) 20 | const user = userEvent.setup() 21 | await user.click(button) 22 | expect(mockFn).toHaveBeenCalledWith('LOGOUT') 23 | }) 24 | 25 | it('should call navigate after timeout', async () => { 26 | vi.useFakeTimers() 27 | render() 28 | vi.advanceTimersByTime(2500) 29 | expect(mockFn).toHaveBeenCalledWith('LOGOUT') 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /src/components/dialogs/index.ts: -------------------------------------------------------------------------------- 1 | export { Dialog } from './Dialog' 2 | -------------------------------------------------------------------------------- /src/components/layout/LoadingOverlay.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useLoadingOverlayStore } from '@/stores' 3 | import { Backdrop, Paper } from '@mui/material' 4 | import { Spinner } from '@pagopa/interop-fe-commons' 5 | 6 | const _LoadingOverlay: React.FC = () => { 7 | const isLoadingOverlayShown = useLoadingOverlayStore((state) => state.isShown) 8 | const loadingOverlayMessage = useLoadingOverlayStore((state) => state.message) 9 | 10 | if (!isLoadingOverlayShown) return null 11 | 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | ) 19 | } 20 | export const LoadingOverlay = React.memo(_LoadingOverlay) 21 | -------------------------------------------------------------------------------- /src/components/layout/SideNav/__tests__/SideNav.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | mockUseCurrentRoute, 4 | mockUseGetActiveUserParty, 5 | mockUseJwt, 6 | renderWithApplicationContext, 7 | } from '@/utils/testing.utils' 8 | import { SideNav } from '../SideNav' 9 | import userEvent from '@testing-library/user-event' 10 | import { vi } from 'vitest' 11 | import * as useIsRouteInCurrentSubtree from '../hooks/useIsRouteInCurrentSubtree' 12 | 13 | mockUseCurrentRoute({ routeKey: 'TOS' }) 14 | vi.spyOn(useIsRouteInCurrentSubtree, 'useIsRouteInCurrentSubtree').mockReturnValue(() => false) 15 | 16 | describe('SideNav', () => { 17 | it('should toggle collapsable menu items', async () => { 18 | mockUseJwt({ isAdmin: true, currentRoles: ['admin'] }) 19 | mockUseGetActiveUserParty() 20 | const screen = renderWithApplicationContext(, { 21 | withRouterContext: true, 22 | }) 23 | const user = userEvent.setup() 24 | 25 | const collapsable = screen.getByRole('button', { name: 'PROVIDE' }) 26 | 27 | await user.click(collapsable) 28 | 29 | const link = screen.queryByRole('link', { name: 'PROVIDE_ESERVICE_LIST' }) 30 | expect(link).toBeInTheDocument() 31 | 32 | await user.click(collapsable) 33 | expect(link).not.toBeInTheDocument() 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /src/components/layout/SideNav/hooks/useIsRouteInCurrentSubtree.tsx: -------------------------------------------------------------------------------- 1 | import { type RouteKey, getParentRoutes, useCurrentRoute } from '@/router' 2 | 3 | export function useIsRouteInCurrentSubtree() { 4 | const { routeKey: currentRouteKey } = useCurrentRoute() 5 | return (routeKey: RouteKey) => { 6 | return [...getParentRoutes(currentRouteKey), currentRouteKey].includes(routeKey) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/components/layout/SideNav/index.ts: -------------------------------------------------------------------------------- 1 | export * from './SideNav' 2 | -------------------------------------------------------------------------------- /src/components/layout/containers/PageBottomActionsContainer.tsx: -------------------------------------------------------------------------------- 1 | import { Stack } from '@mui/material' 2 | import React from 'react' 3 | 4 | type PageBottomActionsContainerProps = { 5 | children: React.ReactNode 6 | } 7 | 8 | export const PageBottomActionsContainer: React.FC = ({ 9 | children, 10 | }) => { 11 | return ( 12 | 13 | {children} 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/components/layout/containers/PageBottomCardContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Box, Stack, Typography } from '@mui/material' 3 | import type { SkeletonProps } from '@mui/material' 4 | import { SectionContainer, SectionContainerSkeleton } from './SectionContainer' 5 | 6 | type PageBottomActionsCardProps = { 7 | title: string 8 | description?: string 9 | children: React.ReactNode 10 | } 11 | 12 | export const PageBottomActionsCardContainer: React.FC = ({ 13 | title, 14 | description, 15 | children, 16 | }) => { 17 | return ( 18 | 19 | 20 | 21 | {title} 22 | 23 | {description && {description}} 24 | 25 | 26 | {children} 27 | 28 | 29 | ) 30 | } 31 | 32 | export const PageBottomActionsCardContainerSkeleton: React.FC = (props) => { 33 | return 34 | } 35 | -------------------------------------------------------------------------------- /src/components/layout/containers/__tests__/AttributeGroupContainer.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { fireEvent, render } from '@testing-library/react' 3 | import { AttributeGroupContainer } from '../AttributeGroupContainer' 4 | import { vi } from 'vitest' 5 | 6 | describe('AttributeGroupContainer', () => { 7 | it('should call on remove action', () => { 8 | const removeFn = vi.fn() 9 | const screen = render( 10 | 11 | <> 12 | 13 | ) 14 | const removeButton = screen.getByRole('button', { name: 'removeGroupAriaLabel' }) 15 | fireEvent.click(removeButton) 16 | expect(removeFn).toHaveBeenCalled() 17 | }) 18 | // it('should match snapshot with footer content', () => { 19 | // const { baseElement } = render( 20 | // }> 21 | // {} 22 | // 23 | // ) 24 | // expect(baseElement).toMatchSnapshot() 25 | // }) 26 | // it('should match snapshot with both footer and header content', () => { 27 | // const { baseElement } = render( 28 | // } footerContent={<>}> 29 | // {} 30 | // 31 | // ) 32 | // expect(baseElement).toMatchSnapshot() 33 | // }) 34 | }) 35 | -------------------------------------------------------------------------------- /src/components/layout/containers/__tests__/DocumentContainer.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from '@testing-library/react' 3 | import { DocumentContainer } from '../DocumentContainer' 4 | import type { EServiceDoc } from '@/api/api.generatedTypes' 5 | import { vi } from 'vitest' 6 | import userEvent from '@testing-library/user-event' 7 | 8 | const docMock: EServiceDoc = { 9 | id: '1', 10 | name: 'name', 11 | contentType: 'pdf', 12 | prettyName: 'document', 13 | checksum: 'test', 14 | } 15 | 16 | describe('DocumentContainer', () => { 17 | it('should correctly edit document name', async () => { 18 | const onUpdateDescription = vi.fn() 19 | const screen = render( 20 | 21 | ) 22 | const user = userEvent.setup() 23 | 24 | const editButton = screen.getByRole('button', { name: 'editDocumentName' }) 25 | await user.click(editButton) 26 | const input = screen.getByRole('textbox', { name: 'prettyName.label' }) 27 | await user.type(input, ' new name') 28 | await user.tab() 29 | expect(onUpdateDescription).toBeCalledWith('document new name') 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /src/components/layout/containers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PageBottomActionsContainer' 2 | export * from './SectionContainer' 3 | export * from './PageContainer' 4 | export * from './AttributeContainer' 5 | export * from './AttributeGroupContainer' 6 | -------------------------------------------------------------------------------- /src/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | export { Footer } from './Footer' 2 | export { Header } from './Header' 3 | export { AppLayout } from './AppLayout' 4 | export { SideNav } from './SideNav' 5 | export { LoadingOverlay } from './LoadingOverlay' 6 | export { ToastNotification } from './ToastNotification' 7 | -------------------------------------------------------------------------------- /src/components/shared/Accordion.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | Accordion as MUIAccordion, 4 | AccordionDetails, 5 | AccordionSummary, 6 | Typography, 7 | } from '@mui/material' 8 | import ExpandMoreIcon from '@mui/icons-material/ExpandMore' 9 | 10 | export type AccordionEntry = { 11 | summary: string | JSX.Element 12 | summarySecondary?: string | JSX.Element 13 | details: string | JSX.Element 14 | } 15 | 16 | type StyledAccordionProps = { 17 | entries: Array 18 | } 19 | 20 | export function Accordion({ entries }: StyledAccordionProps) { 21 | return ( 22 | <> 23 | {entries.map(({ summary, summarySecondary, details }, i: number) => ( 24 | 25 | } 27 | aria-controls={`panel-content-${i}`} 28 | id={`panel-header-${i}`} 29 | sx={{ px: 0 }} 30 | > 31 | 38 | {summary} 39 | 40 | {summarySecondary && ( 41 | 42 | {summarySecondary} 43 | 44 | )} 45 | 46 | {details} 47 | 48 | ))} 49 | 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /src/components/shared/AddAttributesToForm/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AddAttributesToForm' 2 | -------------------------------------------------------------------------------- /src/components/shared/ApiInfoSection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Trans, useTranslation } from 'react-i18next' 3 | import { SectionContainer, SectionContainerSkeleton } from '../layout/containers' 4 | import { Stack, type SkeletonProps } from '@mui/material' 5 | import { Link } from '@/router' 6 | import { InformationContainer } from '@pagopa/interop-fe-commons' 7 | 8 | type ApiInfoSectionProps = { 9 | ids: Array<{ name: string; id: string }> 10 | } 11 | 12 | export const ApiInfoSection: React.FC = ({ ids }) => { 13 | const { t } = useTranslation('shared-components', { 14 | keyPrefix: 'apiInfoSection', 15 | }) 16 | 17 | return ( 18 | }}>{t('content')} 22 | } 23 | > 24 | 25 | {ids.map((element, index) => ( 26 | 33 | ))} 34 | 35 | 36 | ) 37 | } 38 | 39 | export const ApiInfoSectionSkeleton: React.FC = (props) => { 40 | return 41 | } 42 | -------------------------------------------------------------------------------- /src/components/shared/ByDelegationChip.tsx: -------------------------------------------------------------------------------- 1 | import { Chip, Skeleton } from '@mui/material' 2 | import React from 'react' 3 | import { useTranslation } from 'react-i18next' 4 | import { match } from 'ts-pattern' 5 | 6 | type ByDelegationChipProps = { 7 | tenantRole?: 'DELEGATOR' | 'DELEGATE' 8 | } 9 | 10 | export const ByDelegationChip: React.FC = ({ tenantRole }) => { 11 | const { t } = useTranslation('shared-components', { keyPrefix: 'byDelegationChip' }) 12 | 13 | const tenantRoleLabel = match(tenantRole) 14 | .with('DELEGATOR', () => t('label.delegator')) 15 | .with('DELEGATE', () => t('label.delegate')) 16 | .with(undefined, () => t('label.default')) 17 | .exhaustive() 18 | 19 | return 20 | } 21 | 22 | export const ByDelegationChipSkeleton: React.FC = () => { 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /src/components/shared/ClientTable/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ClientTable' 2 | -------------------------------------------------------------------------------- /src/components/shared/DelegationTable/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DelegationsTable' 2 | -------------------------------------------------------------------------------- /src/components/shared/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { QueryErrorResetBoundary } from '@tanstack/react-query' 3 | import { ErrorBoundary as _ErrorBoundary, FallbackProps } from 'react-error-boundary' 4 | 5 | interface ErrorBoundaryProps { 6 | children: React.ReactNode 7 | FallbackComponent: React.ComponentType 8 | } 9 | 10 | export const ErrorBoundary: React.FC = ({ children, FallbackComponent }) => { 11 | return ( 12 | 13 | {({ reset }) => ( 14 | <_ErrorBoundary onReset={reset} FallbackComponent={FallbackComponent}> 15 | {children} 16 | 17 | )} 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /src/components/shared/EserviceTemplate/EServiceTemplateUsefulLinksSection.tsx: -------------------------------------------------------------------------------- 1 | import { SectionContainer } from '@/components/layout/containers' 2 | import { IconLink } from '@/components/shared/IconLink' 3 | import { WELL_KNOWN_URLS } from '@/config/env' 4 | import { Stack } from '@mui/material' 5 | import React from 'react' 6 | import LaunchIcon from '@mui/icons-material/Launch' 7 | import { useTranslation } from 'react-i18next' 8 | 9 | export const EServiceTemplateUsefulLinksSection: React.FC = () => { 10 | const { t } = useTranslation('template', { 11 | keyPrefix: 'read.sections.technicalInformations', 12 | }) 13 | 14 | return ( 15 | 16 | 17 | } 21 | > 22 | {t('usefulLinks.implementAndManageEServiceTemplate')} 23 | 24 | } 28 | > 29 | {t('usefulLinks.updateEserviceInstance')} 30 | 31 | } 35 | > 36 | {t('usefulLinks.wellKnown')} 37 | 38 | 39 | 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /src/components/shared/EserviceTemplate/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceTemplateAttributes' 2 | export * from './EServiceTemplateGeneralInfoSection' 3 | export * from './EServiceTemplateTechnicalInfoSection' 4 | export * from './EServiceTemplateDocumentationSection' 5 | export * from './EServiceTemplateUsefulLinksSection' 6 | export * from './EServiceTemplateUpdateDocumentationDrawer' 7 | export * from './EServiceTemplateAttributes' 8 | export * from './EServiceTemplateVersionSelectorDrawer' 9 | -------------------------------------------------------------------------------- /src/components/shared/FirstLoadingSpinner.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Spinner } from '@pagopa/interop-fe-commons' 3 | 4 | export const FirstLoadingSpinner: React.FC = () => { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /src/components/shared/HeadSection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HeadSection' 2 | -------------------------------------------------------------------------------- /src/components/shared/IconLink.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link, Stack, type LinkProps } from '@mui/material' 3 | 4 | type IconLinkProps = { 5 | startIcon?: React.ReactNode 6 | endIcon?: React.ReactNode 7 | children: React.ReactNode 8 | component?: D 9 | } & LinkProps 10 | 11 | export const IconLink = ({ 12 | startIcon, 13 | endIcon, 14 | children, 15 | ...linkProps 16 | }: IconLinkProps) => { 17 | return ( 18 | 24 | 36 | {startIcon} {children} {endIcon} 37 | 38 | 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /src/components/shared/InfoTooltip.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import InfoIcon from '@mui/icons-material/InfoOutlined' 3 | import { Skeleton, Tooltip } from '@mui/material' 4 | 5 | type InfoTooltipProps = { 6 | label: string 7 | } 8 | 9 | export const InfoTooltip: React.FC = ({ label }) => { 10 | return ( 11 | 12 | 13 | 14 | ) 15 | } 16 | 17 | export const InfoTooltipSkeleton: React.FC = () => { 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /src/components/shared/InputWrapper.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { FormControl, FormHelperText } from '@mui/material' 3 | import type { Theme, SxProps } from '@mui/material' 4 | 5 | type InputWrapperProps = { 6 | error?: string 7 | infoLabel?: React.ReactNode 8 | sx?: SxProps 9 | children: React.ReactNode 10 | infoLabelId?: string 11 | errorId?: string 12 | component?: React.ElementType 13 | } 14 | 15 | export const InputWrapper: React.FC = ({ 16 | infoLabelId, 17 | errorId, 18 | error, 19 | sx, 20 | infoLabel, 21 | children, 22 | component = 'div', 23 | }) => { 24 | const helperTextSx = { fontWeight: 400, color: 'text.secondary', ml: 0, display: 'block' } 25 | 26 | return ( 27 | 28 | {children} 29 | 30 | {error && ( 31 | 32 | {error} 33 | 34 | )} 35 | {infoLabel && ( 36 | 37 | {infoLabel} 38 | 39 | )} 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/components/shared/KeychainsTable/index.ts: -------------------------------------------------------------------------------- 1 | export * from './KeychainsTable' 2 | -------------------------------------------------------------------------------- /src/components/shared/MUI-skeletons/ButtonSkeleton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ButtonProps, Skeleton, SkeletonProps } from '@mui/material' 3 | 4 | type ButtonSkeletonProps = Omit & { 5 | width: number 6 | size?: ButtonProps['size'] 7 | } 8 | 9 | const height: Record, number> = { 10 | small: 40, 11 | medium: 48, 12 | large: 56, 13 | } 14 | 15 | export const ButtonSkeleton: React.FC = ({ 16 | width, 17 | size = 'medium', 18 | sx, 19 | ...props 20 | }) => { 21 | return ( 22 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/components/shared/MUI-skeletons/TabListSkeleton.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from '@mui/material' 2 | import React from 'react' 3 | 4 | export const TabListSkeleton: React.FC = () => { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /src/components/shared/MUI-skeletons/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ButtonSkeleton' 2 | export * from './TabListSkeleton' 3 | -------------------------------------------------------------------------------- /src/components/shared/MaintenanceBanner.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Alert, AlertTitle, Snackbar } from '@mui/material' 3 | import { useMaintenanceBanner } from '@/hooks/useMaintenanceBanner' 4 | 5 | export const MaintenanceBanner: React.FC = () => { 6 | const id = React.useId() 7 | 8 | const { title, text, isOpen, closeBanner } = useMaintenanceBanner() 9 | 10 | return ( 11 | 16 | 23 | 24 | {title} 25 | 26 | {text} 27 | 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/components/shared/RiskAnalysisFormComponents/__test__/RiskAnalysisSwitch.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { FormProvider, useForm } from 'react-hook-form' 3 | import { RiskAnalysisSwitch } from '../RiskAnalysisSwitch' 4 | import { render } from '@testing-library/react' 5 | 6 | type RiskAnalysisSwitchProps = React.ComponentProps 7 | 8 | const renderRiskAnalysisSwitch = (props?: Partial) => { 9 | const propsWithDefaults: RiskAnalysisSwitchProps = { 10 | label: 'RiskAnalysisSwitch', 11 | name: 'RiskAnalysisSwitch', 12 | options: [], 13 | rules: {}, 14 | sx: {}, 15 | ...props, 16 | } 17 | 18 | const Wrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => { 19 | const formMethods = useForm({ 20 | defaultValues: { 21 | RiskAnalysisSwitch: false, 22 | }, 23 | }) 24 | 25 | return {children} 26 | } 27 | 28 | return render(, { wrapper: Wrapper }) 29 | } 30 | 31 | describe('RiskAnalysisSwitch', () => { 32 | it('should change value when clicked', () => { 33 | const { getByRole } = renderRiskAnalysisSwitch() 34 | const checkbox = getByRole('checkbox') 35 | expect(checkbox).not.toBeChecked() 36 | checkbox.click() 37 | expect(checkbox).toBeChecked() 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /src/components/shared/RiskAnalysisFormComponents/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RiskAnalysisFormComponents' 2 | -------------------------------------------------------------------------------- /src/components/shared/Stepper.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Stepper as MUIStepper, Step, StepLabel, Box } from '@mui/material' 3 | import type { StepperStep } from '@/types/common.types' 4 | 5 | type StepperProps = { 6 | steps: Array 7 | activeIndex: number 8 | } 9 | 10 | export function Stepper({ steps, activeIndex }: StepperProps) { 11 | return ( 12 | 13 | 14 | {steps.map(({ label }) => ( 15 | 16 | {label} 17 | 18 | ))} 19 | 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/components/shared/__tests__/Drawer.test.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react' 2 | import React from 'react' 3 | import { Drawer } from '../Drawer' 4 | import { vi } from 'vitest' 5 | import { Typography } from '@mui/material' 6 | 7 | describe('Drawer test', () => { 8 | it('Should not render when isOpen is false', () => { 9 | const { container } = render( 10 | 11 | TEST CHILDREN 12 | 13 | ) 14 | 15 | expect(container).toBeEmptyDOMElement() 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /src/components/shared/__tests__/InfoTooltip.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, waitFor } from '@testing-library/react' 3 | import { InfoTooltip, InfoTooltipSkeleton } from '@/components/shared/InfoTooltip' 4 | import userEvent from '@testing-library/user-event' 5 | 6 | describe("Checks that InfoTooltip snapshots don't change", () => { 7 | it('renders the tooltip message correctly while hovering', async () => { 8 | const user = userEvent.setup() 9 | const infoTooltip = render() 10 | 11 | const icon = infoTooltip.queryByLabelText('label') 12 | await user.hover(icon!) 13 | 14 | // This awaits that the tooltip has been shown 15 | await waitFor(() => { 16 | expect(infoTooltip.baseElement).toHaveTextContent('label') 17 | }) 18 | }) 19 | }) 20 | 21 | describe('InfoTooltipSkeleton', () => { 22 | it('should render correctly', () => { 23 | const { baseElement } = render() 24 | expect(baseElement).toBeInTheDocument() 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /src/components/shared/react-hook-form-inputs/RHFAutocomplete/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RHFAutocompleteMultiple' 2 | export * from './RHFAutocompleteSingle' 3 | -------------------------------------------------------------------------------- /src/components/shared/react-hook-form-inputs/__tests__/RHFCheckbox.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from '@testing-library/react' 3 | import userEvent from '@testing-library/user-event' 4 | 5 | import { TestInputWrapper } from '@/components/shared/react-hook-form-inputs/__tests__/test-utils' 6 | import { RHFCheckbox } from '@/components/shared/react-hook-form-inputs' 7 | 8 | const checkbox = { 9 | standard: { 10 | label: 'label', 11 | name: 'test', 12 | }, 13 | } 14 | 15 | describe('determine whether the integration between react-hook-form and MUI’s Checkbox works', () => { 16 | it('gets the input from the user correctly', async () => { 17 | const user = userEvent.setup() 18 | const checkboxResult = render( 19 | 20 | 21 | 22 | ) 23 | 24 | const checkboxInput = checkboxResult.getByRole('checkbox') 25 | expect(checkboxInput).not.toBeChecked() 26 | 27 | await user.click(checkboxInput) 28 | expect(checkboxInput).toBeChecked() 29 | 30 | await user.click(checkboxInput) 31 | expect(checkboxInput).not.toBeChecked() 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /src/components/shared/react-hook-form-inputs/__tests__/RHFSwitch.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from '@testing-library/react' 3 | import userEvent from '@testing-library/user-event' 4 | 5 | import { TestInputWrapper } from '@/components/shared/react-hook-form-inputs/__tests__/test-utils' 6 | import { RHFSwitch } from '@/components/shared/react-hook-form-inputs' 7 | 8 | const switchProps = { 9 | standard: { 10 | label: 'label', 11 | name: 'test', 12 | }, 13 | } 14 | 15 | describe('determine whether the integration between react-hook-form and MUI’s Switch works', () => { 16 | it('gets the input from the user correctly', async () => { 17 | const user = userEvent.setup() 18 | const switchResult = render( 19 | 20 | 21 | 22 | ) 23 | 24 | const switchInput = switchResult.getByRole('checkbox') 25 | expect(switchInput).not.toBeChecked() 26 | 27 | await user.click(switchInput) 28 | expect(switchInput).toBeChecked() 29 | 30 | await user.click(switchInput) 31 | expect(switchInput).not.toBeChecked() 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /src/components/shared/react-hook-form-inputs/__tests__/test-utils.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useForm, FormProvider } from 'react-hook-form' 3 | 4 | export const TestInputWrapper = ({ children }: { children: React.ReactNode }) => { 5 | const formMethods = useForm<{ 6 | testText: string 7 | test: false 8 | testFile: File | null 9 | checkedItems: Array 10 | }>({ 11 | defaultValues: { 12 | testText: '', 13 | test: false, 14 | testFile: null, 15 | checkedItems: [], 16 | }, 17 | }) 18 | 19 | return {children} 20 | } 21 | -------------------------------------------------------------------------------- /src/components/shared/react-hook-form-inputs/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RHFTextField' 2 | export * from './RHFAutocomplete' 3 | export * from './RHFSwitch' 4 | export * from './RHFRadioGroup' 5 | export * from './RHFCheckboxGroup' 6 | export * from './RHFSingleFileInput' 7 | export * from './RHFCheckbox' 8 | -------------------------------------------------------------------------------- /src/hooks/__tests__/useClientKind.test.ts: -------------------------------------------------------------------------------- 1 | import type { ClientKind } from '@/api/api.generatedTypes' 2 | import { renderHookWithApplicationContext } from '@/utils/testing.utils' 3 | import { throws } from 'assert' 4 | import { createMemoryHistory } from 'history' 5 | import { useClientKind } from '../useClientKind' 6 | 7 | function renderUseGetClientActionsHook(clientKind?: ClientKind) { 8 | const memoryHistory = createMemoryHistory() 9 | 10 | memoryHistory.push('/it/fruizione/catalogo-e-service') 11 | 12 | if (clientKind === 'API') memoryHistory.push('/it/fruizione/interop-m2m') 13 | 14 | if (clientKind === 'CONSUMER') memoryHistory.push('/it/fruizione/client') 15 | 16 | return renderHookWithApplicationContext( 17 | () => useClientKind(), 18 | { 19 | withReactQueryContext: true, 20 | withRouterContext: true, 21 | }, 22 | memoryHistory 23 | ) 24 | } 25 | 26 | describe('check if useClientKind returns the correct kind based on the location path', () => { 27 | it('shoud throw an error if the location path is outside client routes', () => { 28 | throws(() => { 29 | renderUseGetClientActionsHook() 30 | }) 31 | }) 32 | 33 | it('shoud return API client kind if the location path contain interop-m2m', () => { 34 | const { result } = renderUseGetClientActionsHook('API') 35 | expect(result.current).toBe('API') 36 | }) 37 | 38 | it('shoud return API client kind if the location path contain client', () => { 39 | const { result } = renderUseGetClientActionsHook('CONSUMER') 40 | expect(result.current).toBe('CONSUMER') 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /src/hooks/__tests__/useDrawerState.test.tsx: -------------------------------------------------------------------------------- 1 | import { act, renderHook } from '@testing-library/react' 2 | import { useDrawerState } from '../useDrawerState' 3 | 4 | describe('useDrawerState', () => { 5 | it('should return the drawer state and the functions to open and close it', () => { 6 | const { result } = renderHook(() => useDrawerState()) 7 | 8 | expect(result.current).toEqual({ 9 | isOpen: false, 10 | openDrawer: expect.any(Function), 11 | closeDrawer: expect.any(Function), 12 | }) 13 | }) 14 | 15 | describe('openDrawer', () => { 16 | it('should set the drawer state to true', () => { 17 | const { result } = renderHook(() => useDrawerState()) 18 | 19 | act(() => { 20 | result.current.openDrawer() 21 | }) 22 | 23 | expect(result.current.isOpen).toBe(true) 24 | }) 25 | }) 26 | 27 | describe('closeDrawer', () => { 28 | it('should set the drawer state to false', () => { 29 | const { result } = renderHook(() => useDrawerState()) 30 | 31 | act(() => { 32 | result.current.openDrawer() 33 | }) 34 | 35 | expect(result.current.isOpen).toBe(true) 36 | 37 | act(() => { 38 | result.current.closeDrawer() 39 | }) 40 | 41 | expect(result.current.isOpen).toBe(false) 42 | }) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /src/hooks/useActiveStep.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import isEmpty from 'lodash/isEmpty' 3 | import { useLocation } from 'react-router-dom' 4 | 5 | export type ActiveStepProps = { 6 | back: VoidFunction 7 | forward: VoidFunction 8 | activeStep: number 9 | } 10 | 11 | export function scrollToTop() { 12 | window.scrollTo({ top: 0, left: 0, behavior: 'auto' }) 13 | } 14 | 15 | export const useActiveStep = (): ActiveStepProps => { 16 | const location = useLocation() 17 | const [activeStep, setActiveStep] = React.useState(() => { 18 | const locationState: Record = location.state as Record 19 | 20 | if (!isEmpty(locationState) && locationState.stepIndexDestination) { 21 | return locationState.stepIndexDestination as number 22 | } 23 | 24 | return 0 25 | }) 26 | 27 | /* 28 | * Stepper actions 29 | */ 30 | const back = React.useCallback(() => { 31 | setActiveStep((prev) => Math.max(0, prev - 1)) 32 | }, []) 33 | 34 | const forward = React.useCallback(() => { 35 | setActiveStep((prev) => prev + 1) 36 | scrollToTop() 37 | }, []) 38 | 39 | return { back, forward, activeStep } 40 | } 41 | -------------------------------------------------------------------------------- /src/hooks/useActiveTab.ts: -------------------------------------------------------------------------------- 1 | import { useSearchParams } from 'react-router-dom' 2 | 3 | export const useActiveTab = (defaultTab: string) => { 4 | const [searchParams, setSearchParams] = useSearchParams({ tab: defaultTab }) 5 | 6 | const activeTab = searchParams.get('tab') || defaultTab 7 | 8 | const updateActiveTab = (_: unknown, newTab: string) => { 9 | setSearchParams({ ...Object.fromEntries(searchParams), tab: newTab }) 10 | } 11 | 12 | return { activeTab, updateActiveTab } 13 | } 14 | -------------------------------------------------------------------------------- /src/hooks/useCheckRiskAnalysisVersionMismatch.ts: -------------------------------------------------------------------------------- 1 | import type { Purpose } from '@/api/api.generatedTypes' 2 | import { PurposeQueries } from '@/api/purpose' 3 | import { useQuery } from '@tanstack/react-query' 4 | 5 | /** 6 | * Check if the risk analysis version of the purpose is different from the risk analysis latest version. 7 | */ 8 | export function useCheckRiskAnalysisVersionMismatch(purpose: Purpose | undefined): boolean { 9 | const { data: latestRiskAnalysis } = useQuery( 10 | PurposeQueries.getRiskAnalysisLatest({ tenantKind: purpose?.consumer.kind }) 11 | ) 12 | 13 | return Boolean( 14 | !!purpose?.riskAnalysisForm && 15 | latestRiskAnalysis && 16 | purpose.riskAnalysisForm.version !== latestRiskAnalysis.version 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /src/hooks/useClientKind.ts: -------------------------------------------------------------------------------- 1 | import type { ClientKind } from '@/api/api.generatedTypes' 2 | import { useLocation } from 'react-router-dom' 3 | 4 | /** 5 | * Returns the client kind based on the current route 6 | * @returns 7 | * - `API` is returned if we are in the `interop-m2m` sub-route 8 | * - `CONSUMER` is returned if we are in the `client` sub-route 9 | * 10 | */ 11 | export function useClientKind(): ClientKind { 12 | const { pathname } = useLocation() 13 | 14 | if (pathname.includes('interop-m2m')) { 15 | return 'API' 16 | } 17 | 18 | if (pathname.includes('client')) { 19 | return 'CONSUMER' 20 | } 21 | 22 | throw new Error('useClientKind has been called outside client routes') 23 | } 24 | -------------------------------------------------------------------------------- /src/hooks/useCurrentLanguage.ts: -------------------------------------------------------------------------------- 1 | import type { LangCode } from '@/types/common.types' 2 | import { useTranslation } from 'react-i18next' 3 | 4 | function useCurrentLanguage() { 5 | const { i18n } = useTranslation() 6 | return i18n.language as LangCode 7 | } 8 | 9 | export default useCurrentLanguage 10 | -------------------------------------------------------------------------------- /src/hooks/useDrawerState.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** 4 | * This is an utility hook to manage the drawer state. 5 | * 6 | * @returns An object containing the drawer state and the functions to open and close it. 7 | */ 8 | export function useDrawerState() { 9 | const [isOpen, setIsOpen] = React.useState(false) 10 | 11 | const openDrawer = React.useCallback(() => setIsOpen(true), []) 12 | const closeDrawer = React.useCallback(() => setIsOpen(false), []) 13 | 14 | return { isOpen, openDrawer, closeDrawer } 15 | } 16 | -------------------------------------------------------------------------------- /src/hooks/useGetClientActions.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { ClientMutations } from '@/api/client' 3 | import { useClientKind } from './useClientKind' 4 | import { useNavigate } from '@/router' 5 | import type { ActionItemButton } from '@/types/common.types' 6 | import type { Client, CompactClient } from '@/api/api.generatedTypes' 7 | import { AuthHooks } from '@/api/auth' 8 | import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline' 9 | 10 | function useGetClientActions(client?: Client | CompactClient): { 11 | actions: Array 12 | } { 13 | const { t } = useTranslation('common', { keyPrefix: 'actions' }) 14 | const clientKind = useClientKind() 15 | const { isAdmin } = AuthHooks.useJwt() 16 | const navigate = useNavigate() 17 | const { mutate: deleteClient } = ClientMutations.useDelete() 18 | 19 | if (!client || !isAdmin) return { actions: [] } 20 | 21 | function handleDeleteClient() { 22 | if (!client) return 23 | deleteClient( 24 | { clientId: client.id }, 25 | { 26 | onSuccess() { 27 | const successRouteKey = 28 | clientKind === 'API' ? 'SUBSCRIBE_INTEROP_M2M' : 'SUBSCRIBE_CLIENT_LIST' 29 | navigate(successRouteKey) 30 | }, 31 | } 32 | ) 33 | } 34 | 35 | const deleteClientAction: ActionItemButton = { 36 | action: handleDeleteClient, 37 | label: t('delete'), 38 | icon: DeleteOutlineIcon, 39 | color: 'error', 40 | } 41 | 42 | return { actions: [deleteClientAction] } 43 | } 44 | 45 | export default useGetClientActions 46 | -------------------------------------------------------------------------------- /src/hooks/useGetKeychainActions.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import { KeychainMutations } from '@/api/keychain' 3 | import { useNavigate } from '@/router' 4 | import type { ActionItemButton } from '@/types/common.types' 5 | import type { ProducerKeychain, CompactProducerKeychain } from '@/api/api.generatedTypes' 6 | import { AuthHooks } from '@/api/auth' 7 | import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline' 8 | 9 | function useGetKeychainActions(keychain?: ProducerKeychain | CompactProducerKeychain): { 10 | actions: Array 11 | } { 12 | const { t } = useTranslation('common', { keyPrefix: 'actions' }) 13 | const { isAdmin } = AuthHooks.useJwt() 14 | const navigate = useNavigate() 15 | const { mutate: deleteKeychain } = KeychainMutations.useDeleteKeychain() 16 | 17 | if (!keychain || !isAdmin) return { actions: [] } 18 | 19 | function handleDeleteKeychain() { 20 | if (!keychain) return 21 | deleteKeychain( 22 | { producerKeychainId: keychain.id }, 23 | { 24 | onSuccess() { 25 | const successRouteKey = 'PROVIDE_KEYCHAINS_LIST' 26 | navigate(successRouteKey) 27 | }, 28 | } 29 | ) 30 | } 31 | 32 | const deleteKeychainAction: ActionItemButton = { 33 | action: handleDeleteKeychain, 34 | label: t('delete'), 35 | icon: DeleteOutlineIcon, 36 | color: 'error', 37 | } 38 | 39 | return { actions: [deleteKeychainAction] } 40 | } 41 | 42 | export default useGetKeychainActions 43 | -------------------------------------------------------------------------------- /src/hooks/useGetProducerDelegationUserRole.ts: -------------------------------------------------------------------------------- 1 | import { DelegationQueries } from '@/api/delegation' 2 | import { useQuery } from '@tanstack/react-query' 3 | 4 | export function useGetProducerDelegationUserRole({ 5 | eserviceId, 6 | organizationId, 7 | }: { 8 | eserviceId: string | undefined 9 | organizationId: string | undefined 10 | }) { 11 | const { data: producerDelegations = [] } = useQuery({ 12 | ...DelegationQueries.getList({ 13 | eserviceIds: [eserviceId as string], 14 | states: ['ACTIVE'], 15 | kind: 'DELEGATED_PRODUCER', 16 | offset: 0, 17 | limit: 50, 18 | }), 19 | enabled: Boolean(eserviceId), 20 | select: (delegations) => delegations.results ?? [], 21 | }) 22 | 23 | const isDelegate = Boolean( 24 | organizationId && 25 | producerDelegations?.find((delegation) => delegation.delegate.id === organizationId) 26 | ) 27 | 28 | const isDelegator = Boolean( 29 | organizationId && 30 | producerDelegations?.find((delegation) => delegation.delegator.id === organizationId) 31 | ) 32 | 33 | return { 34 | isDelegate, 35 | isDelegator, 36 | producerDelegations, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #root { 4 | height: 100%; 5 | display: flex; 6 | flex-direction: column; 7 | } 8 | 9 | /* 10 | * TEMP Waiting for MUI to reset the body color 11 | */ 12 | body { 13 | background-color: white !important; 14 | } 15 | 16 | pre { 17 | white-space: pre-line; 18 | } 19 | 20 | fieldset { 21 | border: none; 22 | } -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import '@/index.css' 5 | import '@/config/react-i18next' 6 | 7 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( 8 | 9 | 10 | 11 | ) 12 | -------------------------------------------------------------------------------- /src/pages/AssistanceTenantSelectionErrorPage/AssistanceTenantSelectionError.page.tsx: -------------------------------------------------------------------------------- 1 | import { AssistencePartySelectionError } from '@/utils/errors.utils' 2 | 3 | const AssistanceTenantSelectionErrorPage = () => { 4 | /** 5 | * The error boundary will catch the error thrown and will show the 6 | * proper error message. 7 | */ 8 | throw new AssistencePartySelectionError() 9 | } 10 | 11 | export default AssistanceTenantSelectionErrorPage 12 | -------------------------------------------------------------------------------- /src/pages/AssistanceTenantSelectionErrorPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AssistanceTenantSelectionErrorPage } from './AssistanceTenantSelectionError.page' 2 | -------------------------------------------------------------------------------- /src/pages/AssistanceTenantSelectionPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AssistanceTenantSelectionPage } from './AssistanceTenantSelection.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerAgreementCreatePage/hooks/useGetConsumerAgreementCreateAlertProps.ts: -------------------------------------------------------------------------------- 1 | import type { Agreement } from '@/api/api.generatedTypes' 2 | import { useDialog } from '@/stores' 3 | import { isNewEServiceVersionAvailable } from '@/utils/agreement.utils' 4 | import type { AlertProps } from '@mui/material' 5 | import { useTranslation } from 'react-i18next' 6 | 7 | export function useGetConsumerAgreementCreateAlertProps(agreement: Agreement | undefined): 8 | | { 9 | severity: AlertProps['severity'] 10 | content: string 11 | action?: VoidFunction 12 | } 13 | | undefined { 14 | const { t } = useTranslation('agreement') 15 | 16 | const { openDialog } = useDialog() 17 | 18 | if (!agreement || agreement.state !== 'DRAFT') return undefined 19 | 20 | if (isNewEServiceVersionAvailable(agreement)) { 21 | return { 22 | severity: 'warning', 23 | content: t('edit.newVersionAlert'), 24 | } 25 | } 26 | 27 | const hasSetContactEmail = agreement && !!agreement?.consumer.contactMail?.address 28 | if (!hasSetContactEmail) { 29 | return { 30 | severity: 'warning', 31 | content: t('edit.noContactEmailAlert'), 32 | action: () => openDialog({ type: 'setTenantMail' }), 33 | } 34 | } 35 | 36 | const isDelegated = agreement && agreement.delegation 37 | if (isDelegated) { 38 | return { 39 | severity: 'info', 40 | content: t('edit.delegatedAttributesAlert', { delegatorName: agreement.consumer.name }), 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/pages/ConsumerAgreementCreatePage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerAgreementCreatePage } from './ConsumerAgreementCreate.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerAgreementDetailsPage/components/ConsumerAgreementDetailsAttributesSectionsList/ConsumerAgreementDetailsAttributesSectionsList.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useConsumerAgreementDetailsContext } from '../ConsumerAgreementDetailsContext' 3 | import { Stack } from '@mui/material' 4 | import { SectionContainerSkeleton } from '@/components/layout/containers' 5 | import { ConsumerAgreementDetailsCertifiedAttributesSection } from './ConsumerAgreementDetailsCertifiedAttributesSection' 6 | import { ConsumerAgreementDetailsDeclaredAttributesSection } from './ConsumerAgreementDetailsDeclaredAttributesSection' 7 | import { ConsumerAgreementDetailsVerifiedAttributesSection } from './ConsumerAgreementDetailsVerifiedAttributesSection/ConsumerAgreementDetailsVerifiedAttributesSection' 8 | 9 | export const ConsumerAgreementDetailsAttributesSectionsList: React.FC = () => { 10 | const { agreement } = useConsumerAgreementDetailsContext() 11 | 12 | const isPending = agreement.state === 'PENDING' 13 | 14 | return ( 15 | 16 | {!isPending && } 17 | 18 | 19 | 20 | ) 21 | } 22 | 23 | export const ConsumerAgreementDetailsAttributesSectionsListSkeleton: React.FC = () => { 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /src/pages/ConsumerAgreementDetailsPage/components/ConsumerAgreementDetailsContext.tsx: -------------------------------------------------------------------------------- 1 | import type { Agreement, DescriptorAttributes } from '@/api/api.generatedTypes' 2 | import { EServiceQueries } from '@/api/eservice' 3 | import { createContext } from '@/utils/common.utils' 4 | import { useSuspenseQuery } from '@tanstack/react-query' 5 | import React from 'react' 6 | 7 | type ConsumerAgreementDetailsContextType = { 8 | agreement: Agreement 9 | descriptorAttributes: DescriptorAttributes 10 | } 11 | 12 | const initialState: ConsumerAgreementDetailsContextType = { 13 | agreement: undefined!, 14 | descriptorAttributes: undefined!, 15 | } 16 | 17 | const { useContext, Provider } = createContext( 18 | 'ProviderAgreementDetailsContext', 19 | initialState 20 | ) 21 | 22 | const ConsumerAgreementDetailsContextProvider: React.FC<{ 23 | agreement: Agreement 24 | children: React.ReactNode 25 | }> = ({ agreement, children }) => { 26 | // This should not stay here, waiting to get the attributes from the agreement itself 27 | 28 | const { data: descriptor } = useSuspenseQuery( 29 | EServiceQueries.getDescriptorCatalog(agreement.eservice.id, agreement.descriptorId) 30 | ) 31 | 32 | const providerValue = React.useMemo( 33 | () => ({ 34 | agreement, 35 | descriptorAttributes: descriptor.attributes, 36 | }), 37 | [agreement, descriptor] 38 | ) 39 | 40 | return {children} 41 | } 42 | 43 | export { useContext as useConsumerAgreementDetailsContext, ConsumerAgreementDetailsContextProvider } 44 | -------------------------------------------------------------------------------- /src/pages/ConsumerAgreementDetailsPage/components/ConsumerAgreementDetailsGeneralInfoSection/ConsumerAgreementDetailsContactDrawer.tsx: -------------------------------------------------------------------------------- 1 | import type { Mail } from '@/api/api.generatedTypes' 2 | import { Drawer } from '@/components/shared/Drawer' 3 | import { Stack } from '@mui/material' 4 | import { InformationContainer } from '@pagopa/interop-fe-commons' 5 | import React from 'react' 6 | import { useTranslation } from 'react-i18next' 7 | 8 | type ConsumerAgreementDetailsContactDrawerProps = { 9 | isOpen: boolean 10 | onClose: VoidFunction 11 | contact: Mail 12 | } 13 | 14 | export const ConsumerAgreementDetailsContactDrawer: React.FC< 15 | ConsumerAgreementDetailsContactDrawerProps 16 | > = ({ isOpen, onClose, contact }) => { 17 | const { t } = useTranslation('agreement', { 18 | keyPrefix: 'consumerRead.sections.generalInformations.contactDrawer', 19 | }) 20 | 21 | const handleCloseDrawer = () => { 22 | onClose() 23 | } 24 | 25 | return ( 26 | 27 | 28 | 33 | {contact.description && ( 34 | 39 | )} 40 | 41 | 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/pages/ConsumerAgreementDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerAgreementDetailsPage } from './ConsumerAgreementDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerAgreementsListPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ConsumerAgreementsTableRow' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerAgreementsListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerAgreementsListPage } from './ConsumerAgreementsList.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientCreatePage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerClientCreatePage } from './ConsumerClientCreate.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientListPage/ConsumerClientList.page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PageContainer } from '@/components/layout/containers' 3 | import { useTranslation } from 'react-i18next' 4 | import { ClientTable } from '@/components/shared/ClientTable' 5 | import { useNavigate } from '@/router' 6 | import { AuthHooks } from '@/api/auth' 7 | import type { ActionItemButton } from '@/types/common.types' 8 | import PlusOneIcon from '@mui/icons-material/PlusOne' 9 | 10 | const ConsumerClientListPage: React.FC = () => { 11 | const { t } = useTranslation('pages', { keyPrefix: 'consumerClientList' }) 12 | const { t: tCommon } = useTranslation('common') 13 | const navigate = useNavigate() 14 | const { isAdmin } = AuthHooks.useJwt() 15 | 16 | const topSideActions: Array = [ 17 | { 18 | action: () => navigate('SUBSCRIBE_CLIENT_CREATE'), 19 | label: tCommon('createNewBtn'), 20 | variant: 'contained', 21 | icon: PlusOneIcon, 22 | }, 23 | ] 24 | 25 | return ( 26 | 31 | 32 | 33 | ) 34 | } 35 | 36 | export default ConsumerClientListPage 37 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerClientListPage } from './ConsumerClientList.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientM2MListPage/ConsumerClientM2MList.page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PageContainer } from '@/components/layout/containers' 3 | import { useTranslation } from 'react-i18next' 4 | import { ClientTable } from '@/components/shared/ClientTable' 5 | import { useNavigate } from '@/router' 6 | import { AuthHooks } from '@/api/auth' 7 | import type { ActionItemButton } from '@/types/common.types' 8 | import PlusOneIcon from '@mui/icons-material/PlusOne' 9 | 10 | const ConsumerClientM2MListPage: React.FC = () => { 11 | const { t } = useTranslation('pages', { keyPrefix: 'consumerClientM2MList' }) 12 | const { t: tCommon } = useTranslation('common') 13 | const navigate = useNavigate() 14 | const { isAdmin } = AuthHooks.useJwt() 15 | 16 | const topSideActions: Array = [ 17 | { 18 | action: () => navigate('SUBSCRIBE_INTEROP_M2M_CLIENT_CREATE'), 19 | label: tCommon('createNewBtn'), 20 | variant: 'contained', 21 | icon: PlusOneIcon, 22 | }, 23 | ] 24 | 25 | return ( 26 | 31 | 32 | 33 | ) 34 | } 35 | 36 | export default ConsumerClientM2MListPage 37 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientM2MListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerClientM2MListPage } from './ConsumerClientM2MList.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientManagePage/components/ClientOperators/ClientOperatorsTable.tsx: -------------------------------------------------------------------------------- 1 | import { ClientQueries } from '@/api/client' 2 | import { Table } from '@pagopa/interop-fe-commons' 3 | import React from 'react' 4 | import { useTranslation } from 'react-i18next' 5 | import { ClientOperatorsTableRow, ClientOperatorsTableRowSkeleton } from './ClientOperatorsTableRow' 6 | import { useSuspenseQuery } from '@tanstack/react-query' 7 | 8 | type ClientOperatorsTableProps = { 9 | clientId: string 10 | } 11 | 12 | export const ClientOperatorsTable: React.FC = ({ clientId }) => { 13 | const { t: tCommon } = useTranslation('common') 14 | 15 | const { data: operators } = useSuspenseQuery(ClientQueries.getOperatorsList(clientId)) 16 | 17 | const headLabels = [tCommon('table.headData.userName'), ''] 18 | const isEmpty = operators.length === 0 19 | 20 | return ( 21 | 22 | {operators.map((operator) => ( 23 | 24 | ))} 25 |
26 | ) 27 | } 28 | 29 | export const ClientOperatorsTableSkeleton: React.FC = () => { 30 | const { t: tCommon } = useTranslation('common') 31 | 32 | const headLabels = [tCommon('table.headData.userName'), ''] 33 | return ( 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientManagePage/components/ClientOperators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ClientOperators' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientManagePage/components/ClientPublicKeys/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ClientPublicKeys' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientManagePage/components/VoucherInstructions/VoucherInstructionsStep1/index.ts: -------------------------------------------------------------------------------- 1 | export * from './VoucherInstructionsStep1' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientManagePage/components/VoucherInstructions/index.ts: -------------------------------------------------------------------------------- 1 | export * from './VoucherInstructions' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerClientManagePage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerClientManagePage } from './ConsumerClientManage.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerDebugVoucherPage/components/DebugVoucherResults.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Button, Stack } from '@mui/material' 2 | import React from 'react' 3 | import { useTranslation } from 'react-i18next' 4 | import { DebugVoucherResultsStepsSection } from './DebugVoucherResultsStepsSection' 5 | import { DebugVoucherResultsRequestSection } from './DebugVoucherResultsRequestSection' 6 | import DebugVoucherStepDrawer from './DebugVoucherStepDrawer' 7 | import { useDebugVoucherContext } from '../DebugVoucherContext' 8 | import DebugVoucherResultsAlert from './DebugVoucherResultsAlert' 9 | 10 | export const DebugVoucherResults: React.FC = () => { 11 | const { t } = useTranslation('voucher', { keyPrefix: 'consumerDebugVoucher.result' }) 12 | 13 | const { handleMakeNewRequest } = useDebugVoucherContext() 14 | 15 | return ( 16 | <> 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /src/pages/ConsumerDebugVoucherPage/components/DebugVoucherResultsRequestSection.tsx: -------------------------------------------------------------------------------- 1 | import { SectionContainer } from '@/components/layout/containers' 2 | import { InformationContainer } from '@pagopa/interop-fe-commons' 3 | import React from 'react' 4 | import { Stack } from '@mui/material' 5 | import { useDebugVoucherContext } from '../DebugVoucherContext' 6 | import { useTranslation } from 'react-i18next' 7 | 8 | export const DebugVoucherResultsRequestSection: React.FC = () => { 9 | const { t } = useTranslation('voucher', { 10 | keyPrefix: 'consumerDebugVoucher.result.requestSection', 11 | }) 12 | const { request } = useDebugVoucherContext() 13 | 14 | return ( 15 | 16 | 17 | {request.client_id && ( 18 | 19 | )} 20 | 24 | 29 | 34 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /src/pages/ConsumerDebugVoucherPage/components/DebugVoucherResultsStepsSection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTranslation } from 'react-i18next' 3 | import { DebugVoucherResultsStep } from './DebugVoucherResultsStep' 4 | import { SectionContainer } from '@/components/layout/containers' 5 | import { Stack } from '@mui/material' 6 | import { useDebugVoucherContext } from '../DebugVoucherContext' 7 | 8 | export const DebugVoucherResultsStepsSection: React.FC = () => { 9 | const { t } = useTranslation('voucher', { keyPrefix: 'consumerDebugVoucher.result.stepSection' }) 10 | 11 | const { response } = useDebugVoucherContext() 12 | 13 | return ( 14 | 15 | 16 | 20 | 24 | 28 | {response.clientKind === 'CONSUMER' && ( 29 | 33 | )} 34 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /src/pages/ConsumerDebugVoucherPage/components/__test__/DebugVoucherResultsStep.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { mockDebugVoucherContext } from './test.commons' 3 | import { createMockDebugVoucherResultStep } from '@/../__mocks__/data/voucher.mocks' 4 | import { fireEvent, render } from '@testing-library/react' 5 | import { vi } from 'vitest' 6 | import { DebugVoucherResultsStep } from '../DebugVoucherResultsStep' 7 | 8 | describe('DebugVoucherResultsStep testing', () => { 9 | it('should setDebugVoucherStepDrawer function be called on click', () => { 10 | const setDebugVoucherStepDrawerMockFn = vi.fn() 11 | mockDebugVoucherContext({ setDebugVoucherStepDrawer: setDebugVoucherStepDrawerMockFn }) 12 | 13 | const screen = render( 14 | 18 | ) 19 | 20 | expect( 21 | screen.getByRole('button', { name: 'label.clientAssertionValidation chipLabel.failed' }) 22 | ).toBeInTheDocument() 23 | 24 | fireEvent.click( 25 | screen.getByRole('button', { name: 'label.clientAssertionValidation chipLabel.failed' }) 26 | ) 27 | 28 | expect(setDebugVoucherStepDrawerMockFn).toBeCalled() 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /src/pages/ConsumerDebugVoucherPage/components/__test__/test.commons.ts: -------------------------------------------------------------------------------- 1 | import { vi } from 'vitest' 2 | import * as debugVoucherContext from '../../DebugVoucherContext' 3 | 4 | export function mockDebugVoucherContext( 5 | returnValue: Partial> 6 | ) { 7 | vi.spyOn(debugVoucherContext, 'useDebugVoucherContext').mockReturnValue( 8 | returnValue as ReturnType 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src/pages/ConsumerDebugVoucherPage/hooks/useGetDebugVoucherResultChipProps.ts: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next' 2 | import type { ChipProps } from '@mui/material' 3 | import type { TokenGenerationValidationEntry } from '@/api/api.generatedTypes' 4 | 5 | export function useGetDebugVoucherResultChipProps( 6 | step?: TokenGenerationValidationEntry 7 | ): ChipProps | undefined { 8 | const { t } = useTranslation('voucher', { keyPrefix: 'consumerDebugVoucher.result' }) 9 | 10 | if (!step) { 11 | return 12 | } 13 | 14 | if (step.result === 'PASSED') { 15 | return { 16 | label: t('chipLabel.passed'), 17 | color: 'success', 18 | } 19 | } 20 | 21 | if (step.result === 'SKIPPED') { 22 | return { 23 | label: t('chipLabel.skipped'), 24 | color: 'warning', 25 | } 26 | } 27 | 28 | if (step.result === 'FAILED') { 29 | return { 30 | label: t('chipLabel.failed', { 31 | count: step.failures.length, 32 | }), 33 | color: 'error', 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/pages/ConsumerDebugVoucherPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerDebugVoucherPage } from './ConsumerDebugVoucher.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceCatalogPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceCatalogGrid' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceCatalogPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerEServiceCatalogPage } from './ConsumerEServiceCatalog.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceDetailsPage/components/ConsumerEServiceDescriptorAttributes.tsx: -------------------------------------------------------------------------------- 1 | import { EServiceQueries } from '@/api/eservice' 2 | import { SectionContainer, SectionContainerSkeleton } from '@/components/layout/containers' 3 | import { ReadOnlyDescriptorAttributes } from '@/components/shared/ReadOnlyDescriptorAttributes' 4 | import { useParams } from '@/router' 5 | import { useSuspenseQuery } from '@tanstack/react-query' 6 | import React from 'react' 7 | import { useTranslation } from 'react-i18next' 8 | 9 | export const ConsumerEServiceDescriptorAttributes: React.FC = () => { 10 | const { t } = useTranslation('eservice', { keyPrefix: 'read.sections.attributes' }) 11 | 12 | const { eserviceId, descriptorId } = useParams<'SUBSCRIBE_CATALOG_VIEW'>() 13 | const { data: descriptorAttributes } = useSuspenseQuery({ 14 | ...EServiceQueries.getDescriptorCatalog(eserviceId, descriptorId), 15 | select: (d) => d.attributes, 16 | }) 17 | 18 | return ( 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | export const ConsumerEServiceDescriptorAttributesSkeleton: React.FC = () => { 26 | return 27 | } 28 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceDetailsPage/components/ConsumerEServiceDetailsAlerts.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { CatalogEServiceDescriptor } from '@/api/api.generatedTypes' 3 | import { Alert, Stack } from '@mui/material' 4 | import { useTranslation } from 'react-i18next' 5 | 6 | type ConsumerEServiceDetailsAlertsProps = { 7 | descriptor: CatalogEServiceDescriptor | undefined 8 | } 9 | 10 | export const ConsumerEServiceDetailsAlerts: React.FC = ({ 11 | descriptor, 12 | }) => { 13 | const { t } = useTranslation('eservice', { keyPrefix: 'read.alert' }) 14 | 15 | if (!descriptor) return null 16 | 17 | const isSuspended = descriptor?.state === 'SUSPENDED' 18 | const isDeprecated = descriptor?.state === 'DEPRECATED' 19 | 20 | return ( 21 | 22 | {isSuspended && {t('suspended')}} 23 | {isDeprecated && {t('deprecated')}} 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceDetailsPage/components/ConsumerEServiceGeneralInfoSection/ConsumerEServiceProducerContactsDrawer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { CatalogEServiceDescriptor } from '@/api/api.generatedTypes' 3 | import { Stack } from '@mui/material' 4 | import { InformationContainer } from '@pagopa/interop-fe-commons' 5 | import { useTranslation } from 'react-i18next' 6 | import { Drawer } from '@/components/shared/Drawer' 7 | 8 | type ConsumerEServiceProducerContactsDrawerProps = { 9 | isOpen: boolean 10 | onClose: VoidFunction 11 | descriptor: CatalogEServiceDescriptor 12 | } 13 | 14 | export const ConsumerEServiceProducerContactsDrawer: React.FC< 15 | ConsumerEServiceProducerContactsDrawerProps 16 | > = ({ isOpen, onClose, descriptor }) => { 17 | const { t } = useTranslation('eservice', { keyPrefix: 'read.drawers.producerContactsInfoDrawer' }) 18 | 19 | if (!descriptor.eservice.mail) return null 20 | 21 | return ( 22 | 23 | 24 | 29 | {descriptor.eservice.mail.description && ( 30 | 35 | )} 36 | 37 | 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceDetailsPage/components/ConsumerEServiceGeneralInfoSection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ConsumerEServiceGeneralInfoSection' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerEServiceDetailsPage } from './ConsumerEServiceDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceTemplateDetailsPage/components/ConsumerEServiceTemplateDetails.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Grid } from '@mui/material' 3 | import { 4 | EServiceTemplateGeneralInfoSection, 5 | EServiceTemplateAttributes, 6 | EServiceTemplateTechnicalInfoSection, 7 | } from '@/components/shared/EserviceTemplate' 8 | 9 | export const ConsumerEServiceTemplateDetails: React.FC = () => { 10 | const readyonly = true 11 | const routeKey = 'SUBSCRIBE_ESERVICE_TEMPLATE_DETAILS' 12 | 13 | return ( 14 | <> 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceTemplateDetailsPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ConsumerEServiceTemplateDetails' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerEServiceTemplateDetailsPage/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pagopa/pdnd-interop-frontend/8fc60fc8db18e12c3f51eef4c6628b5c446e47c3/src/pages/ConsumerEServiceTemplateDetailsPage/index.ts -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeCreatePage/ConsumerPurposeCreate.page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PageContainer } from '@/components/layout/containers' 3 | import { useTranslation } from 'react-i18next' 4 | import { PurposeCreateForm } from './components/PurposeCreateForm' 5 | 6 | const ConsumerPurposeCreatePage: React.FC = () => { 7 | const { t } = useTranslation('purpose') 8 | 9 | return ( 10 | 17 | 18 | 19 | ) 20 | } 21 | 22 | export default ConsumerPurposeCreatePage 23 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeCreatePage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerPurposeCreatePage } from './ConsumerPurposeCreate.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeDetailsPage/components/PurposeClientsTab/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PurposeClientsTab' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeDetailsPage/components/PurposeDetailsTab/PurposeDetailsTab.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { Purpose } from '@/api/api.generatedTypes' 3 | import { ConsumerPurposeDetailsGeneralInfoSection } from './ConsumerPurposeDetailsGeneralInfoSection' 4 | import { ConsumerPurposeDetailsLoadEstimateSection } from './ConsumerPurposeDetailsLoadEstimateSection' 5 | import { SectionContainerSkeleton } from '@/components/layout/containers' 6 | import { Stack } from '@mui/material' 7 | 8 | interface PurposeDetailsTabProps { 9 | purpose: Purpose 10 | openRejectReasonDrawer: VoidFunction 11 | } 12 | 13 | export const PurposeDetailsTab: React.FC = ({ 14 | purpose, 15 | openRejectReasonDrawer, 16 | }) => { 17 | return ( 18 | 19 | 20 | 24 | 25 | ) 26 | } 27 | 28 | export const PurposeDetailTabSkeleton: React.FC = () => { 29 | return ( 30 | 31 | 32 | 33 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeDetailsPage/components/PurposeDetailsTab/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PurposeDetailsTab' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerPurposeDetailsPage } from './ConsumerPurposeDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeEditPage/components/PurposeEditStepGeneral/PurposeEditStepGeneral.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PurposeQueries } from '@/api/purpose' 3 | import { useParams } from '@/router' 4 | import { NotFoundError } from '@/utils/errors.utils' 5 | import PurposeEditStepGeneralForm, { 6 | PurposeEditStepGeneralFormSkeleton, 7 | type PurposeEditStepGeneralFormValues, 8 | } from './PurposeEditStepGeneralForm' 9 | import type { ActiveStepProps } from '@/hooks/useActiveStep' 10 | import { useQuery } from '@tanstack/react-query' 11 | 12 | export const PurposeEditStepGeneral: React.FC = (props) => { 13 | const { purposeId } = useParams<'SUBSCRIBE_PURPOSE_EDIT'>() 14 | const { data: purpose, isLoading: isLoadingPurpose } = useQuery( 15 | PurposeQueries.getSingle(purposeId) 16 | ) 17 | 18 | if (isLoadingPurpose) { 19 | return 20 | } 21 | 22 | if (!purpose) { 23 | throw new NotFoundError() 24 | } 25 | 26 | const defaultValues: PurposeEditStepGeneralFormValues = { 27 | title: purpose.title, 28 | description: purpose.description, 29 | dailyCalls: purpose.versions[0]?.dailyCalls ?? 1, 30 | isFreeOfCharge: purpose.isFreeOfCharge ? 'YES' : 'NO', 31 | freeOfChargeReason: purpose.freeOfChargeReason, 32 | } 33 | 34 | return 35 | } 36 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeEditPage/components/PurposeEditStepGeneral/index.ts: -------------------------------------------------------------------------------- 1 | export { PurposeEditStepGeneral } from './PurposeEditStepGeneral' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeEditPage/components/PurposeEditStepRiskAnalysis/RiskAnalysisForm/RiskAnalysisVersionMismatchDialog.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material' 2 | import React from 'react' 3 | import { useTranslation } from 'react-i18next' 4 | 5 | type RiskAnalysisVersionMismatchDialogProps = { 6 | onProceed: VoidFunction 7 | onRefuse: VoidFunction 8 | } 9 | 10 | export const RiskAnalysisVersionMismatchDialog: React.FC< 11 | RiskAnalysisVersionMismatchDialogProps 12 | > = ({ onProceed, onRefuse }) => { 13 | const { t } = useTranslation('purpose', { 14 | keyPrefix: 'edit.stepRiskAnalysis.versionMismatchDialog', 15 | }) 16 | const ariaLabelId = React.useId() 17 | const ariaDescriptionId = React.useId() 18 | 19 | return ( 20 | 21 | {t('title')} 22 | 23 | {t('description')} 24 | 25 | 26 | 29 | 32 | 33 | 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeEditPage/components/PurposeEditStepRiskAnalysis/RiskAnalysisForm/__tests__/RiskAnalysisVersionMismatchDialog.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from '@testing-library/react' 3 | import { RiskAnalysisVersionMismatchDialog } from '../RiskAnalysisVersionMismatchDialog' 4 | import { vi } from 'vitest' 5 | 6 | describe('RiskAnalysisVersionMismatchDialog', () => { 7 | it('should call onProceed when clicking on proceed button', () => { 8 | const onProceed = vi.fn() 9 | const screen = render( 10 | 11 | ) 12 | screen.getByRole('button', { name: 'proceedButtonLabel' }).click() 13 | expect(onProceed).toHaveBeenCalled() 14 | }) 15 | 16 | it('should call onRefuse when clicking on refuse button', () => { 17 | const onRefuse = vi.fn() 18 | const screen = render( 19 | 20 | ) 21 | screen.getByRole('button', { name: 'cancelButtonLabel' }).click() 22 | expect(onRefuse).toHaveBeenCalled() 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeEditPage/components/PurposeEditStepRiskAnalysis/RiskAnalysisForm/index.ts: -------------------------------------------------------------------------------- 1 | export * from './RiskAnalysisForm' 2 | export * from './RiskAnalysisVersionMismatchDialog' 3 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeEditPage/components/PurposeEditStepRiskAnalysis/index.ts: -------------------------------------------------------------------------------- 1 | export { PurposeEditStepRiskAnalysis } from './PurposeEditStepRiskAnalysis' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeEditPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerPurposeEditPage } from './ConsumerPurposeEdit.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeSummaryPage/components/ConsumerPurposeSummaryRiskAnalysisAccordion.tsx: -------------------------------------------------------------------------------- 1 | import { PurposeQueries } from '@/api/purpose' 2 | import { RiskAnalysisInfoSummary } from '@/components/shared/RiskAnalysisInfoSummary' 3 | import { Alert, Stack } from '@mui/material' 4 | import { useSuspenseQuery } from '@tanstack/react-query' 5 | import React from 'react' 6 | import { useTranslation } from 'react-i18next' 7 | 8 | type ConsumerPurposeSummaryRiskAnalysisAccordionProps = { 9 | purposeId: string 10 | } 11 | 12 | export const ConsumerPurposeSummaryRiskAnalysisAccordion: React.FC< 13 | ConsumerPurposeSummaryRiskAnalysisAccordionProps 14 | > = ({ purposeId }) => { 15 | const { data: purpose } = useSuspenseQuery(PurposeQueries.getSingle(purposeId)) 16 | const { t } = useTranslation('purpose', { keyPrefix: 'summary.riskAnalysisSection' }) 17 | 18 | return ( 19 | <> 20 | 21 | 22 | {purpose.eservice.mode === 'RECEIVE' && ( 23 | 24 | {t('providerRiskAnalysisAlert')} 25 | 26 | )} 27 | 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeSummaryPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ConsumerPurposeSummaryGeneralInformationAccordion' 2 | export * from './ConsumerPurposeSummaryRiskAnalysisAccordion' 3 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeSummaryPage/hooks/useGetConsumerPurposeAlertProps.ts: -------------------------------------------------------------------------------- 1 | import type { Purpose } from '@/api/api.generatedTypes' 2 | import { useCheckRiskAnalysisVersionMismatch } from '@/hooks/useCheckRiskAnalysisVersionMismatch' 3 | import type { AlertProps } from '@mui/material' 4 | import { useTranslation } from 'react-i18next' 5 | import { match } from 'ts-pattern' 6 | 7 | export function useGetConsumerPurposeAlertProps( 8 | purpose: Purpose | undefined 9 | ): AlertProps | undefined { 10 | const { t } = useTranslation('purpose', { keyPrefix: 'summary.alerts' }) 11 | const hasRiskAnalysisVersionMismatch = useCheckRiskAnalysisVersionMismatch(purpose) 12 | 13 | if (!purpose) return 14 | 15 | return match({ 16 | hasRiskAnalysisVersionMismatch, 17 | isAgreementArchived: purpose?.agreement.state === 'ARCHIVED', 18 | isEServiceDescriptorArchived: purpose?.eservice.descriptor.state === 'ARCHIVED', 19 | }) 20 | .returnType() 21 | .with({ hasRiskAnalysisVersionMismatch: true }, () => ({ 22 | severity: 'warning', 23 | children: t('newRiskAnalysisAvailable'), 24 | })) 25 | .with({ isAgreementArchived: true }, { isEServiceDescriptorArchived: true }, () => ({ 26 | severity: 'warning', 27 | children: t('descriptorOrAgreementArchived'), 28 | })) 29 | .otherwise(() => undefined) 30 | } 31 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposeSummaryPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerPurposeSummaryPage } from './ConsumerPurposeSummary.page' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposesListPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ConsumerPurposesTable' 2 | -------------------------------------------------------------------------------- /src/pages/ConsumerPurposesListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ConsumerPurposesListPage } from './ConsumerPurposesList.page' 2 | -------------------------------------------------------------------------------- /src/pages/DelegationCreatePage/index.ts: -------------------------------------------------------------------------------- 1 | export { DelegationCreatePage } from './DelegationCreate.page' 2 | -------------------------------------------------------------------------------- /src/pages/DelegationDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { DelegationDetailsPage } from './DelegationDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/DelegationsPage/components/DelegationsReceivedTab/DelegationsReceivedTab.tsx: -------------------------------------------------------------------------------- 1 | import type { GetDelegationsParams } from '@/api/api.generatedTypes' 2 | import { AuthHooks } from '@/api/auth' 3 | import { Pagination, usePagination } from '@pagopa/interop-fe-commons' 4 | import React from 'react' 5 | import { 6 | DelegationsTable, 7 | DelegationsTableSkeleton, 8 | } from '../../../../components/shared/DelegationTable/DelegationsTable' 9 | import { keepPreviousData, useQuery } from '@tanstack/react-query' 10 | import { DelegationQueries } from '@/api/delegation' 11 | 12 | export const DelegationsReceivedTab: React.FC = () => { 13 | const { jwt } = AuthHooks.useJwt() 14 | const { paginationParams, paginationProps, getTotalPageCount } = usePagination({ limit: 10 }) 15 | 16 | const params: GetDelegationsParams = { 17 | ...paginationParams, 18 | delegateIds: [jwt?.organizationId as string], 19 | } 20 | 21 | const { data: totalPageCount = 0 } = useQuery({ 22 | ...DelegationQueries.getList(params), 23 | placeholderData: keepPreviousData, 24 | enabled: Boolean(jwt?.organizationId), 25 | select: ({ pagination }) => getTotalPageCount(pagination.totalCount), 26 | }) 27 | 28 | return ( 29 | <> 30 | } 32 | > 33 | 34 | 35 | 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/DelegationsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { DelegationsPage } from './Delegations.page' 2 | -------------------------------------------------------------------------------- /src/pages/DeveloperToolsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DeveloperToolsPage } from './DeveloperTools.page' 2 | -------------------------------------------------------------------------------- /src/pages/ErrorPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ErrorPage } from './Error.page' 2 | -------------------------------------------------------------------------------- /src/pages/KeyDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as KeyDetailsPage } from './KeyDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/LogoutPage/Logout.page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { FE_LOGIN_URL } from '@/config/env' 3 | import { STORAGE_KEY_SESSION_TOKEN } from '@/config/constants' 4 | 5 | const LogoutPage: React.FC = () => { 6 | React.useEffect(() => { 7 | window.localStorage.removeItem(STORAGE_KEY_SESSION_TOKEN) 8 | window.location.assign(FE_LOGIN_URL) 9 | }, []) 10 | 11 | return null 12 | } 13 | 14 | export default LogoutPage 15 | -------------------------------------------------------------------------------- /src/pages/LogoutPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as LogoutPage } from './Logout.page' 2 | -------------------------------------------------------------------------------- /src/pages/NotFoundPage/NotFound.page.tsx: -------------------------------------------------------------------------------- 1 | import { PageContainer } from '@/components/layout/containers' 2 | import { Link } from '@/router' 3 | import React from 'react' 4 | import { useTranslation } from 'react-i18next' 5 | 6 | const NotFoundPage: React.FC = () => { 7 | const { t } = useTranslation('error') 8 | 9 | return ( 10 | 15 | 16 | {t('actions.backToHome')} 17 | 18 | 19 | ) 20 | } 21 | 22 | export default NotFoundPage 23 | -------------------------------------------------------------------------------- /src/pages/NotFoundPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as NotFoundPage } from './NotFound.page' 2 | -------------------------------------------------------------------------------- /src/pages/OperatorDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as OperatorDetailsPage } from './OperatorDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/PartyRegistry.page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PageContainer } from '@/components/layout/containers' 3 | import { 4 | PartyContactsSection, 5 | PartyAttributesSection, 6 | PartyContactsSectionSkeleton, 7 | } from './components' 8 | import { AuthHooks } from '@/api/auth' 9 | import { PartyGeneralInfoSection } from './components/PartyGeneralInfoSection' 10 | 11 | const PartyRegistryPage: React.FC = () => { 12 | const { jwt } = AuthHooks.useJwt() 13 | const pageTitle = jwt?.organization.name ?? '' 14 | 15 | return ( 16 | 17 | }> 18 | 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | export default PartyRegistryPage 27 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/components/PartyAttributesSection/AttributesContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SectionContainer } from '@/components/layout/containers' 3 | 4 | export const AttributesContainer: React.FC<{ 5 | title: string 6 | description: React.ReactNode 7 | children: React.ReactNode 8 | }> = ({ title, description, children }) => { 9 | return ( 10 | 11 | {children} 12 | 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/components/PartyAttributesSection/EmptyAttributesAlert.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { AttributeGroupContainer } from '@/components/layout/containers' 3 | import type { AttributeKey } from '@/types/attribute.types' 4 | import { useTranslation } from 'react-i18next' 5 | 6 | export const EmptyAttributesAlert: React.FC<{ type: AttributeKey }> = ({ type }) => { 7 | const { t } = useTranslation('party', { keyPrefix: `attributes.${type}` }) 8 | return 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/components/PartyAttributesSection/PartyAttributesSection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { SectionContainer } from '@/components/layout/containers' 3 | import { Divider, Grid } from '@mui/material' 4 | import { useTranslation } from 'react-i18next' 5 | import { CertifiedAttributes } from './CertifiedPartyAttributes' 6 | import { VerifiedAttributes } from './VerifiedPartyAttributes' 7 | import { DeclaredAttributes } from './DeclaredPartyAttributes' 8 | 9 | export const PartyAttributesSection: React.FC = () => { 10 | const { t } = useTranslation('party', { keyPrefix: 'attributes' }) 11 | 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/components/PartyAttributesSection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PartyAttributesSection' 2 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/components/PartyContactsSection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PartyContactsSection' 2 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/components/PartyGeneralInfoSection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PartyGeneralInfoSection' 2 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PartyAttributesSection' 2 | export * from './PartyContactsSection' 3 | -------------------------------------------------------------------------------- /src/pages/PartyRegistryPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as PartyRegistryPage } from './PartyRegistry.page' 2 | -------------------------------------------------------------------------------- /src/pages/PrivacyPolicyPage/PrivacyPolicy.page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PageContainer } from '@/components/layout/containers' 3 | import { useTranslation } from 'react-i18next' 4 | import { OneTrustNoticesQueries } from '@/api/one-trust-notices' 5 | import { useGeneratePath } from '@/router' 6 | import { parseHtmlJsonToReactNode } from '@/utils/common.utils' 7 | import { useQuery } from '@tanstack/react-query' 8 | import { AuthHooks } from '@/api/auth' 9 | import useCurrentLanguage from '@/hooks/useCurrentLanguage' 10 | 11 | const PrivacyPolicyPage: React.FC = () => { 12 | const { t } = useTranslation('common', { keyPrefix: 'privacyPolicy' }) 13 | const generatePath = useGeneratePath() 14 | const path = generatePath('PRIVACY_POLICY') 15 | 16 | const { jwt } = AuthHooks.useJwt() 17 | const lang = useCurrentLanguage() 18 | 19 | const isAuthenticated = Boolean(jwt) 20 | 21 | const { data: bffPrivacyPolicy } = useQuery( 22 | OneTrustNoticesQueries.getNoticeContent({ 23 | consentType: 'PP', 24 | isAuthenticated, 25 | }) 26 | ) 27 | const { data: bucketPrivacyPolicy } = useQuery( 28 | OneTrustNoticesQueries.getPublicNoticeContent({ 29 | consentType: 'PP', 30 | isAuthenticated, 31 | lang, 32 | }) 33 | ) 34 | 35 | const privacyPolicy = bffPrivacyPolicy || bucketPrivacyPolicy 36 | 37 | return ( 38 | 39 | {privacyPolicy && parseHtmlJsonToReactNode(privacyPolicy, path)} 40 | 41 | ) 42 | } 43 | 44 | export default PrivacyPolicyPage 45 | -------------------------------------------------------------------------------- /src/pages/PrivacyPolicyPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as PrivacyPolicyPage } from './PrivacyPolicy.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderAgreementDetailsPage/components/ProviderAgreementDetailsAttributesSectionsList/ProviderAgreementDetailsAttributesSectionsList.tsx: -------------------------------------------------------------------------------- 1 | import { ProviderAgreementDetailsDeclaredAttributesSection } from './ProviderAgreementDetailsDeclaredAttributesSection' 2 | import { SectionContainerSkeleton } from '@/components/layout/containers' 3 | import { Stack } from '@mui/material' 4 | import React from 'react' 5 | import { ProviderAgreementDetailsCertifiedAttributesSection } from './ProviderAgreementDetailsCertifiedAttributesSection' 6 | import { ProviderAgreementDetailsVerifiedAttributesSection } from './ProviderAgreementDetailsVerifiedAttributesSection/ProviderAgreementDetailsVerifiedAttributesSection' 7 | import { useProviderAgreementDetailsContext } from '../ProviderAgreementDetailsContext' 8 | 9 | export const ProviderAgreementDetailsAttributesSectionsList: React.FC = () => { 10 | const { agreement } = useProviderAgreementDetailsContext() 11 | 12 | const isPending = agreement.state === 'PENDING' 13 | 14 | return ( 15 | 16 | {!isPending && } 17 | 18 | {!isPending && } 19 | 20 | ) 21 | } 22 | 23 | export const ProviderAgreementDetailsAttributesSectionsListSkeleton: React.FC = () => { 24 | return ( 25 | 26 | 27 | 28 | 29 | 30 | ) 31 | } 32 | -------------------------------------------------------------------------------- /src/pages/ProviderAgreementDetailsPage/components/ProviderAgreementDetailsContext.tsx: -------------------------------------------------------------------------------- 1 | import type { Agreement, DescriptorAttributes } from '@/api/api.generatedTypes' 2 | import { EServiceQueries } from '@/api/eservice' 3 | import { createContext } from '@/utils/common.utils' 4 | import { useSuspenseQuery } from '@tanstack/react-query' 5 | import React from 'react' 6 | 7 | type ProviderAgreementDetailsContextType = { 8 | agreement: Agreement 9 | descriptorAttributes: DescriptorAttributes 10 | } 11 | 12 | const initialState: ProviderAgreementDetailsContextType = { 13 | agreement: undefined!, 14 | descriptorAttributes: undefined!, 15 | } 16 | 17 | const { useContext, Provider } = createContext( 18 | 'ProviderAgreementDetailsContext', 19 | initialState 20 | ) 21 | 22 | const ProviderAgreementDetailsContextProvider: React.FC<{ 23 | agreement: Agreement 24 | children: React.ReactNode 25 | }> = ({ agreement, children }) => { 26 | // This should not stay here, waiting to get the attributes from the agreement itself 27 | const { data: descriptor } = useSuspenseQuery( 28 | EServiceQueries.getDescriptorCatalog(agreement.eservice.id, agreement.descriptorId) 29 | ) 30 | 31 | const providerValue = React.useMemo(() => { 32 | const descriptorAttributes = descriptor.attributes 33 | 34 | return { 35 | agreement, 36 | descriptorAttributes, 37 | } 38 | }, [agreement, descriptor]) 39 | 40 | return {children} 41 | } 42 | 43 | export { useContext as useProviderAgreementDetailsContext, ProviderAgreementDetailsContextProvider } 44 | -------------------------------------------------------------------------------- /src/pages/ProviderAgreementDetailsPage/components/ProviderAgreementDetailsGeneralInfoSection/ProviderAgreementDetailsContactDrawer.tsx: -------------------------------------------------------------------------------- 1 | import type { Mail } from '@/api/api.generatedTypes' 2 | import { Drawer } from '@/components/shared/Drawer' 3 | import { Stack } from '@mui/material' 4 | import { InformationContainer } from '@pagopa/interop-fe-commons' 5 | import React from 'react' 6 | import { useTranslation } from 'react-i18next' 7 | 8 | type ProviderAgreementDetailsContactDrawerProps = { 9 | isOpen: boolean 10 | onClose: VoidFunction 11 | contact: Mail 12 | isDelegatedConsumer?: boolean 13 | } 14 | 15 | export const ProviderAgreementDetailsContactDrawer: React.FC< 16 | ProviderAgreementDetailsContactDrawerProps 17 | > = ({ isOpen, onClose, contact, isDelegatedConsumer }) => { 18 | const { t } = useTranslation('agreement', { 19 | keyPrefix: 'providerRead.sections.generalInformations.contactDrawer', 20 | }) 21 | 22 | return ( 23 | 28 | 29 | 34 | {contact.description && ( 35 | 40 | )} 41 | 42 | 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /src/pages/ProviderAgreementDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderAgreementDetailsPage } from './ProviderAgreementDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderAgreementsListPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProviderAgreementsTable' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderAgreementsListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderAgreementsListPage } from './ProviderAgreementsList.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceCreatePage/components/EServiceCreateStepAttributes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceCreateStepAttributes' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceCreatePage/components/EServiceCreateStepDocuments/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceCreateStepDocuments' 2 | export * from './EServiceCreateFromTemplateStepDocuments' 3 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceCreatePage/components/EServiceCreateStepGeneral/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceCreateStepGeneral' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceCreatePage/components/EServiceCreateStepVersion/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceCreateStepVersion' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceCreatePage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceCreatePage } from './ProviderEServiceCreate.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceDetailsPage/components/ProviderEServiceDetailsTab/ProviderEServiceDetailsAlerts.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import type { ProducerEServiceDescriptor } from '@/api/api.generatedTypes' 3 | import { Alert, Stack } from '@mui/material' 4 | import { useTranslation } from 'react-i18next' 5 | 6 | type ProviderEServiceDetailsAlertsProps = { 7 | descriptor: ProducerEServiceDescriptor | undefined 8 | } 9 | 10 | export const ProviderEServiceDetailsAlerts: React.FC = ({ 11 | descriptor, 12 | }) => { 13 | const { t } = useTranslation('eservice', { keyPrefix: 'read.alert' }) 14 | 15 | if (!descriptor) return null 16 | 17 | const isSuspended = descriptor?.state === 'SUSPENDED' 18 | const isDeprecated = descriptor?.state === 'DEPRECATED' 19 | 20 | return ( 21 | 22 | {isSuspended && {t('suspended')}} 23 | {isDeprecated && {t('deprecated')}} 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceDetailsPage/components/ProviderEServiceDetailsTab/ProviderEServiceGeneralInfoSection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProviderEServiceGeneralInfoSection' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceDetailsPage/components/ProviderEServiceDetailsTab/ProviderEServiceTechnicalInfoSection/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProviderEServiceTechnicalInfoSection' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceDetailsPage/components/ProviderEServiceKeychainsTab/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProviderEServiceKeychainsTab' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceDetailsPage } from './ProviderEServiceDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceFromTemplateCreatePage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceFromTemplateCreatePage } from './ProviderEServiceFromTemplateCreate.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceListPage/components/EServiceTable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTranslation } from 'react-i18next' 3 | import { EServiceTableRow, EServiceTableRowSkeleton } from './EServiceTableRow' 4 | import { Table } from '@pagopa/interop-fe-commons' 5 | import type { ProducerEService } from '@/api/api.generatedTypes' 6 | 7 | type EServiceTableProps = { 8 | eservices: Array 9 | } 10 | 11 | export const EServiceTable: React.FC = ({ eservices }) => { 12 | const { t } = useTranslation('common', { keyPrefix: 'table.headData' }) 13 | 14 | const headLabels = [t('eserviceName'), t('version'), t('status'), ''] 15 | 16 | const isEmpty = eservices && eservices.length === 0 17 | 18 | return ( 19 | 20 | {eservices?.map((eservice) => )} 21 |
22 | ) 23 | } 24 | 25 | export const EServiceTableSkeleton: React.FC = () => { 26 | const { t } = useTranslation('common', { keyPrefix: 'table.headData' }) 27 | const headLabels = [t('eserviceName'), t('version'), t('status'), ''] 28 | 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceListPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceTable' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceListPage } from './ProviderEServiceList.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceSummaryPage/components/ProviderEServiceAttributeVersionSummary.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ReadOnlyDescriptorAttributes } from '@/components/shared/ReadOnlyDescriptorAttributes' 3 | import { EServiceQueries } from '@/api/eservice' 4 | import { useParams } from '@/router' 5 | import { useSuspenseQuery } from '@tanstack/react-query' 6 | 7 | export const ProviderEServiceAttributeVersionSummary: React.FC = () => { 8 | const params = useParams<'PROVIDE_ESERVICE_SUMMARY'>() 9 | 10 | const { data: descriptor } = useSuspenseQuery( 11 | EServiceQueries.getDescriptorProvider(params.eserviceId, params.descriptorId) 12 | ) 13 | 14 | if (!descriptor) return null 15 | 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceSummaryPage/components/ProviderEServiceRiskAnalysisSummaryList.tsx: -------------------------------------------------------------------------------- 1 | import { EServiceQueries } from '@/api/eservice' 2 | import { useParams } from '@/router' 3 | import { Divider, Stack, Typography } from '@mui/material' 4 | import React from 'react' 5 | import { useTranslation } from 'react-i18next' 6 | import { useSuspenseQuery } from '@tanstack/react-query' 7 | import { RiskAnalysisInfoSummary } from '@/components/shared/RiskAnalysisInfoSummary' 8 | 9 | export const ProviderEServiceRiskAnalysisSummaryList: React.FC = () => { 10 | const { t } = useTranslation('eservice', { keyPrefix: 'summary.riskAnalysisSummaryList' }) 11 | const params = useParams<'PROVIDE_ESERVICE_SUMMARY'>() 12 | 13 | const { data: descriptor } = useSuspenseQuery( 14 | EServiceQueries.getDescriptorProvider(params.eserviceId, params.descriptorId) 15 | ) 16 | 17 | const riskAnalysisList = descriptor.eservice.riskAnalysis 18 | 19 | return ( 20 | }> 21 | {riskAnalysisList.map((riskAnalysis, index) => ( 22 | 23 | 24 | {t('riskAnalysisTitle', { 25 | riskAnalysisIndex: index + 1, 26 | totalRiskAnalysis: riskAnalysisList.length, 27 | riskAnalysisName: riskAnalysis.name, 28 | })} 29 | 30 | 34 | 35 | ))} 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceSummaryPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProviderEServiceGeneralInfoSummary' 2 | export * from './ProviderEServiceVersionInfoSummary' 3 | export * from './ProviderEServiceDocumentationSummary' 4 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceSummaryPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceSummaryPage } from './ProviderEServiceSummary.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateCreatePage/components/EServiceTemplateCreateStepAttributes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceTemplateCreateStepAttributes' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateCreatePage/components/EServiceTemplateCreateStepVersion/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceTemplateCreateStepVersion' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateCreatePage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceTemplateCreatePage } from './ProviderEServiceTemplateCreate.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateDetailsPage/components/ProviderEServiceTemplateDetailsTab/ProviderEServiceTemplateDetailsTab.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Grid } from '@mui/material' 3 | import { 4 | EServiceTemplateAttributes, 5 | EServiceTemplateGeneralInfoSection, 6 | EServiceTemplateTechnicalInfoSection, 7 | } from '@/components/shared/EserviceTemplate' 8 | import type { EServiceTemplateVersionState } from '@/api/api.generatedTypes' 9 | 10 | type ProviderEServiceDetailsTabProps = { 11 | templateVersionState: EServiceTemplateVersionState | undefined 12 | } 13 | export const ProviderEServiceTemplateDetailsTab: React.FC = ({ 14 | templateVersionState, 15 | }) => { 16 | const readonly = templateVersionState === 'DEPRECATED' 17 | const routeKey = 'PROVIDE_ESERVICE_TEMPLATE_DETAILS' 18 | return ( 19 | <> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateDetailsPage/components/ProviderEServiceTemplateTenantsTab/ProviderEServiceTemplateTenantsTab.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useParams } from '@/router' 3 | import { ProviderEServiceTemplateUsingTenantsTable } from './ProviderEServiceTemplateUsingTenantsTable' 4 | import type { CompactEServiceTemplateVersion } from '@/api/api.generatedTypes' 5 | 6 | type ProviderEServiceTemplateTenantsTabProps = { 7 | templateVersions: CompactEServiceTemplateVersion[] 8 | } 9 | export const ProviderEServiceTemplateTenantsTab: React.FC< 10 | ProviderEServiceTemplateTenantsTabProps 11 | > = ({ templateVersions }) => { 12 | const { eServiceTemplateId } = useParams<'PROVIDE_ESERVICE_TEMPLATE_DETAILS'>() 13 | 14 | return ( 15 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceTemplateDetailsPage } from './ProviderEServiceTemplateDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateSummaryPage/components/ProviderEServiceTemplateAttributeVersionSummary.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ReadOnlyDescriptorAttributes } from '@/components/shared/ReadOnlyDescriptorAttributes' 3 | import { useParams } from '@/router' 4 | import { useSuspenseQuery } from '@tanstack/react-query' 5 | import { TemplateQueries } from '@/api/template' 6 | 7 | export const ProviderEServiceTemplateAttributeVersionSummary: React.FC = () => { 8 | const params = useParams<'PROVIDE_ESERVICE_TEMPLATE_SUMMARY'>() 9 | 10 | const { data: template } = useSuspenseQuery( 11 | TemplateQueries.getSingle(params.eServiceTemplateId, params.eServiceTemplateVersionId) 12 | ) 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateSummaryPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProviderEServiceTemplateGeneralInfoSummary' 2 | export * from './ProviderEServiceTemplateVersionInfoSummary' 3 | export * from './ProviderEServiceTemplateAttributeVersionSummary' 4 | export * from './ProviderEServiceTemplateDocumentationSummary' 5 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplateSummaryPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceTemplateSummaryPage } from './ProviderEServiceTemplateSummary.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplatesCatalogPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EServiceTemplateCatalogGrid' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplatesCatalogPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceTemplatesCatalogPage } from './ProviderEServiceTemplatesCatalog.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplatesListPage/components/TemplateTable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTranslation } from 'react-i18next' 3 | import { TemplateTableRow, TemplateTableRowSkeleton } from './TemplateTableRow' 4 | import { Table } from '@pagopa/interop-fe-commons' 5 | import type { ProducerEServiceTemplate } from '@/api/api.generatedTypes' 6 | 7 | type TemplateTableProps = { 8 | templates: Array 9 | } 10 | 11 | export const TemplateTable: React.FC = ({ templates }) => { 12 | const { t: tCommon } = useTranslation('common', { keyPrefix: 'table.headData' }) 13 | const { t } = useTranslation('template') 14 | 15 | const headLabels = [tCommon('templateName'), tCommon('version'), tCommon('status'), ''] 16 | 17 | const isEmpty = templates && templates.length === 0 18 | 19 | return ( 20 | 21 | {templates?.map((template) => )} 22 |
23 | ) 24 | } 25 | 26 | export const TemplateTableSkeleton: React.FC = () => { 27 | const { t } = useTranslation('common', { keyPrefix: 'table.headData' }) 28 | const headLabels = [t('templateName'), t('version'), t('status'), ''] 29 | 30 | return ( 31 | 32 | 33 | 34 | 35 | 36 | 37 |
38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplatesListPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TemplateTable' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderEServiceTemplatesListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderEServiceTemplatesListPage } from './ProviderEServiceTemplatesList.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderKeychainCreatePage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderKeychainCreatePage } from './ProviderKeychainCreate.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderKeychainDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderKeychainDetailsPage } from './ProviderKeychainDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderKeychainPublicKeyDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderKeychainPublicKeyDetailsPage } from './ProviderKeychainPublicKeyDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderKeychainUserDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderKeychainUserDetailsPage } from './ProviderKeychainUserDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderKeychainsListPage/ProviderKeychainsList.page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PageContainer } from '@/components/layout/containers' 3 | import { useTranslation } from 'react-i18next' 4 | import { useNavigate } from '@/router' 5 | import { AuthHooks } from '@/api/auth' 6 | import type { ActionItemButton } from '@/types/common.types' 7 | import PlusOneIcon from '@mui/icons-material/PlusOne' 8 | import { KeychainsTable } from '@/components/shared/KeychainsTable' 9 | 10 | const ProviderKeychainsListPage: React.FC = () => { 11 | const { t } = useTranslation('pages', { keyPrefix: 'providerKeychainsList' }) 12 | const { t: tCommon } = useTranslation('common') 13 | const navigate = useNavigate() 14 | const { isAdmin } = AuthHooks.useJwt() 15 | 16 | const topSideActions: Array = [ 17 | { 18 | action: () => navigate('PROVIDE_KEYCHAIN_CREATE'), 19 | label: tCommon('createNewBtn'), 20 | variant: 'contained', 21 | icon: PlusOneIcon, 22 | }, 23 | ] 24 | 25 | return ( 26 | 31 | 32 | 33 | ) 34 | } 35 | 36 | export default ProviderKeychainsListPage 37 | -------------------------------------------------------------------------------- /src/pages/ProviderKeychainsListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderKeychainsListPage } from './ProviderKeychainsList.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderPurposeDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderPurposeDetailsPage } from './ProviderPurposeDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderPurposesListPage/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ProviderPurposesTable' 2 | -------------------------------------------------------------------------------- /src/pages/ProviderPurposesListPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProviderPurposesListPage } from './ProviderPurposesList.page' 2 | -------------------------------------------------------------------------------- /src/pages/RiskAnalysisEserviceAssociatedPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as RiskAnalysisEServiceAssociatedPage } from './RiskAnalysisEServiceAssociated.page' 2 | -------------------------------------------------------------------------------- /src/pages/RiskAnalysisExporterToolPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as RiskAnalysisExporterToolPage } from './RiskAnalysisExporterTool.page' 2 | -------------------------------------------------------------------------------- /src/pages/TOSPage/TOS.page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PageContainer } from '@/components/layout/containers' 3 | import { useTranslation } from 'react-i18next' 4 | import { OneTrustNoticesQueries } from '@/api/one-trust-notices' 5 | import { useGeneratePath } from '@/router' 6 | import { parseHtmlJsonToReactNode } from '@/utils/common.utils' 7 | import { useQuery } from '@tanstack/react-query' 8 | import useCurrentLanguage from '@/hooks/useCurrentLanguage' 9 | import { AuthHooks } from '@/api/auth' 10 | 11 | const TOSPage: React.FC = () => { 12 | const { t } = useTranslation('common', { keyPrefix: 'tos' }) 13 | const generatePath = useGeneratePath() 14 | const path = generatePath('TOS') 15 | 16 | const { jwt } = AuthHooks.useJwt() 17 | const lang = useCurrentLanguage() 18 | 19 | const isAuthenticated = Boolean(jwt) 20 | 21 | const { data: bffTermsOfService } = useQuery( 22 | OneTrustNoticesQueries.getNoticeContent({ 23 | consentType: 'TOS', 24 | isAuthenticated, 25 | }) 26 | ) 27 | const { data: bucketTermsOfService } = useQuery( 28 | OneTrustNoticesQueries.getPublicNoticeContent({ 29 | consentType: 'TOS', 30 | isAuthenticated, 31 | lang, 32 | }) 33 | ) 34 | 35 | const termsOfService = bffTermsOfService || bucketTermsOfService 36 | 37 | return ( 38 | 39 | {termsOfService && parseHtmlJsonToReactNode(termsOfService, path)} 40 | 41 | ) 42 | } 43 | 44 | export default TOSPage 45 | -------------------------------------------------------------------------------- /src/pages/TOSPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TOSPage } from './TOS.page' 2 | -------------------------------------------------------------------------------- /src/pages/TenantCertifierAttributeDetailsPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TenantCertifierAttributeDetails } from './TenantCertifierAttributeDetails.page' 2 | -------------------------------------------------------------------------------- /src/pages/TenantCertifierPage/TenantCertifier.page.tsx: -------------------------------------------------------------------------------- 1 | import { PageContainer } from '@/components/layout/containers' 2 | import React from 'react' 3 | import { useActiveTab } from '@/hooks/useActiveTab' 4 | import { TabContext, TabList, TabPanel } from '@mui/lab' 5 | import { Tab } from '@mui/material' 6 | import { ManageAttributesTab } from './components/ManageAttributesTab/ManageAttributesTab' 7 | import { useTranslation } from 'react-i18next' 8 | import { AssignAttributesTab } from './components/AssignAttributesTab/AssignAttributesTab' 9 | 10 | const TenantCertifierPage: React.FC = () => { 11 | const { t: tPages } = useTranslation('pages', { keyPrefix: 'tenantCertifier' }) 12 | const { t } = useTranslation('party', { keyPrefix: 'tenantCertifier' }) 13 | const { activeTab, updateActiveTab } = useActiveTab('manage') 14 | 15 | return ( 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ) 38 | } 39 | 40 | export default TenantCertifierPage 41 | -------------------------------------------------------------------------------- /src/pages/TenantCertifierPage/components/AssignAttributesTab/AttributesTable.tsx: -------------------------------------------------------------------------------- 1 | import type { RequesterCertifiedAttribute } from '@/api/api.generatedTypes' 2 | import { Table } from '@pagopa/interop-fe-commons' 3 | import React from 'react' 4 | import { useTranslation } from 'react-i18next' 5 | import { AttributesTableRow, AttributesTableRowSkeleton } from './AttributesTableRow' 6 | 7 | type AttributesTableProps = { 8 | attributes: Array 9 | } 10 | 11 | export const AttributesTable: React.FC = ({ attributes }) => { 12 | const { t } = useTranslation('common', { keyPrefix: 'table.headData' }) 13 | 14 | const headLabels = [t('assigneeTenant'), t('certifiedAttribute'), ''] 15 | 16 | const isEmpty = attributes && attributes.length === 0 17 | 18 | return ( 19 | 20 | {attributes?.map((attribute) => ( 21 | 22 | ))} 23 |
24 | ) 25 | } 26 | 27 | export const AttributesTableSkeleton: React.FC = () => { 28 | const { t } = useTranslation('common', { keyPrefix: 'table.headData' }) 29 | const headLabels = [t('assigneeTenant'), t('certifiedAttribute'), ''] 30 | 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /src/pages/TenantCertifierPage/components/AssignAttributesTab/AttributesTableRow.tsx: -------------------------------------------------------------------------------- 1 | import type { RequesterCertifiedAttribute } from '@/api/api.generatedTypes' 2 | import { AuthHooks } from '@/api/auth' 3 | import { ButtonSkeleton } from '@/components/shared/MUI-skeletons' 4 | import { useDialog } from '@/stores' 5 | import { Button, Skeleton } from '@mui/material' 6 | import { TableRow } from '@pagopa/interop-fe-commons' 7 | import React from 'react' 8 | import { useTranslation } from 'react-i18next' 9 | 10 | type AttributesTableRowProps = { 11 | attribute: RequesterCertifiedAttribute 12 | } 13 | 14 | export const AttributesTableRow: React.FC = ({ attribute }) => { 15 | const { t } = useTranslation('common') 16 | 17 | const { openDialog } = useDialog() 18 | const { isAdmin } = AuthHooks.useJwt() 19 | 20 | const handleRevoke = () => { 21 | openDialog({ 22 | type: 'revokeCertifiedAttribute', 23 | attribute: attribute, 24 | }) 25 | } 26 | 27 | return ( 28 | 29 | {isAdmin && ( 30 | 33 | )} 34 | 35 | ) 36 | } 37 | 38 | export const AttributesTableRowSkeleton: React.FC = () => { 39 | return ( 40 | , ]}> 41 | 42 | 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /src/pages/TenantCertifierPage/components/ManageAttributesTab/AttributesTable.tsx: -------------------------------------------------------------------------------- 1 | import type { CompactAttribute } from '@/api/api.generatedTypes' 2 | import { Table } from '@pagopa/interop-fe-commons' 3 | import React from 'react' 4 | import { useTranslation } from 'react-i18next' 5 | import { AttributesTableRow, AttributesTableRowSkeleton } from './AttributesTableRow' 6 | 7 | type AttributesTableProps = { 8 | attributes: Array 9 | } 10 | 11 | export const AttributesTable: React.FC = ({ attributes }) => { 12 | const { t } = useTranslation('common', { keyPrefix: 'table.headData' }) 13 | 14 | const headLabels = [t('certifiedAttributes'), ''] 15 | 16 | const isEmpty = attributes && attributes.length === 0 17 | 18 | return ( 19 | 20 | {attributes?.map((attribute) => ( 21 | 22 | ))} 23 |
24 | ) 25 | } 26 | 27 | export const AttributesTableSkeleton: React.FC = () => { 28 | const { t } = useTranslation('common', { keyPrefix: 'table.headData' }) 29 | const headLabels = [t('certifiedAttributes'), ''] 30 | 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /src/pages/TenantCertifierPage/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TenantCertifierPage } from './TenantCertifier.page' 2 | -------------------------------------------------------------------------------- /src/router/__tests__/router.utils.test.ts: -------------------------------------------------------------------------------- 1 | import { isProviderOrConsumerRoute } from '@/router/router.utils' 2 | 3 | describe('checks router utils functions behavior', () => { 4 | it('checks that isProviderOrConsumerRoute return the right string - if provider', () => { 5 | const result = isProviderOrConsumerRoute('/erogazione/test/1') 6 | 7 | expect(result).toEqual('provider') 8 | }) 9 | 10 | it('checks that isProviderOrConsumerRoute return the right string - if consumer', () => { 11 | const result = isProviderOrConsumerRoute('/fruizione/test/1') 12 | 13 | expect(result).toEqual('consumer') 14 | }) 15 | 16 | it('checks that isProviderOrConsumerRoute return the right string - if nor provider or consumer', () => { 17 | const result = isProviderOrConsumerRoute('/test/1') 18 | 19 | expect(result).toBeNull() 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /src/router/components/RouterProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { RouterProvider as _RouterProvider } from 'react-router-dom' 3 | import { router } from '../routes' 4 | 5 | export const RouterProvider: React.FC = () => { 6 | return <_RouterProvider router={router} /> 7 | } 8 | -------------------------------------------------------------------------------- /src/router/components/RoutesWrapper/TOSAgreement.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Trans, useTranslation } from 'react-i18next' 3 | import { TOSAgreement as PagoPATOSAgreement } from '@pagopa/mui-italia' 4 | import { Link } from '@/router' 5 | 6 | type TOSAgreementProps = { 7 | onAcceptAgreement: VoidFunction 8 | } 9 | 10 | const TOSAgreement: React.FC = ({ onAcceptAgreement }) => { 11 | const { t } = useTranslation('pagopa', { keyPrefix: 'tos' }) 12 | 13 | return ( 14 | , 21 | 2: , 22 | }} 23 | > 24 | {t('description')} 25 | 26 | } 27 | onConfirm={onAcceptAgreement} 28 | confirmBtnLabel={t('confirmBtnLabel')} 29 | /> 30 | ) 31 | } 32 | 33 | export default TOSAgreement 34 | -------------------------------------------------------------------------------- /src/router/components/RoutesWrapper/__tests__/TOSAgreement.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { vi } from 'vitest' 3 | import userEvent from '@testing-library/user-event' 4 | import TOSAgreement from '@/router/components/RoutesWrapper/TOSAgreement' 5 | import { renderWithApplicationContext } from '@/utils/testing.utils' 6 | 7 | const mockAcceptAgreement = vi.fn() 8 | 9 | describe('determine whether TOSAgreement works', () => { 10 | it('calls the passed callback on confirm', async () => { 11 | const user = userEvent.setup() 12 | const tosAgreement = renderWithApplicationContext( 13 | , 14 | { 15 | withRouterContext: true, 16 | } 17 | ) 18 | const button = tosAgreement.getByRole('button') 19 | await user.click(button) 20 | 21 | expect(mockAcceptAgreement).toBeCalledTimes(1) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /src/router/components/RoutesWrapper/index.ts: -------------------------------------------------------------------------------- 1 | import RoutesWrapper from './RoutesWrapper' 2 | export default RoutesWrapper 3 | -------------------------------------------------------------------------------- /src/router/hooks/__tests__/useScrollTopOnLocationChange.test.tsx: -------------------------------------------------------------------------------- 1 | import { renderHookWithApplicationContext } from '@/utils/testing.utils' 2 | import { vi } from 'vitest' 3 | import useScrollTopOnLocationChange from '../useScrollTopOnLocationChange' 4 | 5 | const scrollToSpy = vi.spyOn(window, 'scroll') 6 | 7 | describe('useScrollTopOnLocationChange tests', () => { 8 | it('should scroll top on location change', async () => { 9 | const { history, rerender } = renderHookWithApplicationContext( 10 | () => useScrollTopOnLocationChange(), 11 | { 12 | withRouterContext: true, 13 | } 14 | ) 15 | expect(scrollToSpy).toBeCalledTimes(0) 16 | 17 | history.push('/test') 18 | rerender() 19 | 20 | expect(scrollToSpy).toBeCalledTimes(1) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /src/router/hooks/useCheckSessionExpired.ts: -------------------------------------------------------------------------------- 1 | import { useDialog } from '@/stores' 2 | import React from 'react' 3 | import { useCurrentRoute } from '@/router' 4 | import { hasSessionExpired } from '@/utils/common.utils' 5 | 6 | /** 7 | * Checks for session expiration every second. 8 | * Must be used inside router context because it uses the 'useCurrentRoute' hook to check if the current route is public. 9 | * If the current route is public, the session expiration check is not performed. 10 | * If the session has expired, opens the 'sessionExpired' dialog that redirects to the logout page. 11 | */ 12 | export function useCheckSessionExpired(exp?: number) { 13 | const { isPublic } = useCurrentRoute() 14 | const { openDialog } = useDialog() 15 | 16 | const checkSessionExpiredInterval = React.useRef() 17 | 18 | React.useEffect(() => { 19 | const checkSessionExpired = () => { 20 | if (isPublic) return 21 | 22 | if (hasSessionExpired(exp)) { 23 | clearInterval(checkSessionExpiredInterval.current) 24 | openDialog({ type: 'sessionExpired' }) 25 | } 26 | } 27 | 28 | checkSessionExpiredInterval.current = window.setInterval(checkSessionExpired, 1000) 29 | 30 | return () => { 31 | clearInterval(checkSessionExpiredInterval.current) 32 | } 33 | }, [openDialog, isPublic, exp]) 34 | } 35 | -------------------------------------------------------------------------------- /src/router/hooks/useCurrentRoute.ts: -------------------------------------------------------------------------------- 1 | import { isProviderOrConsumerRoute } from '../router.utils' 2 | import { useAuthGuard, useLocation } from '..' 3 | 4 | /** Returns the route informations of the current location */ 5 | export function useCurrentRoute() { 6 | const { isPublic } = useAuthGuard() 7 | const { pathname, routeKey } = useLocation() 8 | 9 | return { 10 | routeKey, 11 | isPublic, 12 | mode: isProviderOrConsumerRoute(pathname), 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/router/hooks/useScrollTopOnLocationChange.ts: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useLocation } from 'react-router-dom' 3 | 4 | function useScrollTopOnLocationChange() { 5 | const location = useLocation() 6 | const prevLocation = React.useRef(location.pathname) 7 | 8 | if (prevLocation.current !== location.pathname) { 9 | window.scroll(0, 0) 10 | prevLocation.current = location.pathname 11 | } 12 | } 13 | 14 | export default useScrollTopOnLocationChange 15 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | export { RouterProvider } from './components/RouterProvider' 2 | export { useCurrentRoute } from './hooks/useCurrentRoute' 3 | 4 | import * as _routes from './routes' 5 | 6 | export const { useNavigate, useParams, useLocation, useAuthGuard, useGeneratePath } = _routes.hooks 7 | export const { Link, Redirect, Breadcrumbs } = _routes.components 8 | export const { getParentRoutes } = _routes.utils 9 | export type RouteKey = _routes.RouteKey 10 | export const routes = _routes.routes 11 | -------------------------------------------------------------------------------- /src/router/router.utils.ts: -------------------------------------------------------------------------------- 1 | import type { ProviderOrConsumer } from '@/types/common.types' 2 | import memoize from 'lodash/memoize' 3 | 4 | export const isProviderOrConsumerRoute = memoize((pathname: string): ProviderOrConsumer | null => { 5 | if (pathname.includes('erogazione')) { 6 | return 'provider' 7 | } 8 | 9 | if (pathname.includes('fruizione')) { 10 | return 'consumer' 11 | } 12 | 13 | return null 14 | }) 15 | -------------------------------------------------------------------------------- /src/static/locales/en/assistance.json: -------------------------------------------------------------------------------- 1 | { 2 | "tenantSelection": { 3 | "title": "Select the party to impersonate", 4 | "subtitle": "If the party does not appear in the list, it means that it has not performed onboarding on the Area Riservata or the first access on PDND Interoperabilità", 5 | "textFieldLabel": "Find party", 6 | "nextButtonLabel": "Next", 7 | "deselectButtonLabel": "Deselect party", 8 | "noResultsLabel": "No results" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/static/locales/en/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": { 3 | "reloadPage": "Reload", 4 | "backToHome": "Back to home", 5 | "retry": "Retry", 6 | "backToSelfcare": "Go back to Area Riservata", 7 | "goToSupportPage": "Go to the Support page" 8 | }, 9 | "default": { 10 | "title": "Unexpected Error", 11 | "description": "An unexpected error has occurred. Please reload the page." 12 | }, 13 | "notFound": { 14 | "title": "404", 15 | "description": "The requested page does not exist" 16 | }, 17 | "axiosError": { 18 | "title": "Server Error", 19 | "description": "There was an error while handling the request. Please reload the page.", 20 | "correlationIdText": "You will find the error id below, copy and paste it into the reporting form on the Support page.", 21 | "tooltipTitle": "Identifier copied successfully" 22 | }, 23 | "forbidden": { 24 | "title": "Not authorized", 25 | "description": "You don't have the necessary permissions to view the page." 26 | }, 27 | "tokenExchange": { 28 | "title": "Authentication error", 29 | "description": "Please, go back to Area Riservata and try again" 30 | }, 31 | "assistencePartySelection": { 32 | "title": "Error: not authorized.", 33 | "description": "You do not have the necessary permissions to view PDND Interoperability in support mode, or your session has expired. Try signing in again from Google Workspace." 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/static/locales/en/key.json: -------------------------------------------------------------------------------- 1 | { 2 | "backToKeyListBtn": "Back to key list", 3 | "edit": { 4 | "orphanAlertLabel": "The user who uploaded this key is no longer part of this client. It is recommended to replace the key with a new one. To find out more, <1>read the guide.", 5 | "generalInformations": { 6 | "title": "General informations", 7 | "creationDateField": { 8 | "label": "Creation date" 9 | }, 10 | "uploaderField": { 11 | "label": "Uploaded by" 12 | }, 13 | "kidField": { 14 | "label": "Key id (kid)", 15 | "copySuccessFeedbackText": "Id copied successfully" 16 | }, 17 | "clientIdField": { 18 | "label": "Client id (clientId)", 19 | "copySuccessFeedbackText": "Id copied successfully" 20 | }, 21 | "goToTechnicalDocButton": "Go to technical documentation" 22 | } 23 | }, 24 | "list": { 25 | "title": "Public keys", 26 | "description": "Public keys belonging to this client will appear in the list.", 27 | "userEnableInfo": "Add yourself as a member of this client before you can upload a key", 28 | "publicKeyLimitInfo": "You have reached the maximum number of keys for this client", 29 | "supportDisableInfo": "The support user is not authorized to add keys" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/static/locales/en/pagopa.json: -------------------------------------------------------------------------------- 1 | { 2 | "tos": { 3 | "title": "Interoperability", 4 | "description": "By entering, you accept the service <1>Terms and Conditions and confirm you have read the <2>Privacy Policy.", 5 | "confirmBtnLabel": "Enter" 6 | }, 7 | "footer": { 8 | "links": { 9 | "privacy": { 10 | "label": "Privacy Policy", 11 | "ariaLabel": "Vai al link: privacy policy" 12 | }, 13 | "dataProtection": { 14 | "label": "Personal data protection rights", 15 | "ariaLabel": "Vai al link: personal data protection rights" 16 | }, 17 | "terms": { 18 | "label": "Terms and conditions", 19 | "ariaLabel": "Vai al link: terms and conditions" 20 | }, 21 | "a11y": { 22 | "label": "Accessibility", 23 | "ariaLabel": "Vai al link: accessibility" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/static/locales/en/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "backToMemberListBtn": "Back to members list", 3 | "clientOperatorsTab": { 4 | "title": "Client members", 5 | "description": "Members belonging to this client will appear in the list. If an API Admin is appointed, each member will be able to perform write operations." 6 | }, 7 | "actionReservedToAdminTooltip": "This actions is reserved to user with administrator permissions", 8 | "actions": { 9 | "removeFromClient": { 10 | "label": "Remove from client", 11 | "tooltip": "The operation is permitted only to users with administrator permissions" 12 | }, 13 | "delete": { 14 | "label": "Delete", 15 | "tooltip": "The operation is permitted only to users with administrator permissions" 16 | } 17 | }, 18 | "edit": { 19 | "generalInformations": "General informations", 20 | "productRoleField": { 21 | "label": "Permissions on PDND Interoperability" 22 | }, 23 | "operatorKeysLink": { 24 | "label": "Go to the view of the keys associated with the user" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/static/locales/it/assistance.json: -------------------------------------------------------------------------------- 1 | { 2 | "tenantSelection": { 3 | "title": "Seleziona l’ente da impersonare", 4 | "subtitle": "Se l’ente non compare nella tendina, significa che non ha effettuato l’onboarding su Area Riservata o il primo accesso su PDND Interoperabilità", 5 | "textFieldLabel": "Cerca ente", 6 | "nextButtonLabel": "Prosegui", 7 | "deselectButtonLabel": "Deseleziona ente", 8 | "noResultsLabel": "Nessun risultato" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/static/locales/it/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": { 3 | "reloadPage": "Ricarica", 4 | "backToHome": "Torna alla home", 5 | "retry": "Riprova", 6 | "backToSelfcare": "Torna ad Area Riservata", 7 | "goToSupportPage": "Vai alla pagina di Assistenza" 8 | }, 9 | "default": { 10 | "title": "Errore inaspettato", 11 | "description": "Si è verificato un errore inaspettato. Per favore, ricarica la pagina." 12 | }, 13 | "notFound": { 14 | "title": "Pagina inesistente", 15 | "description": "404 - La pagina cercata purtroppo non esiste." 16 | }, 17 | "axiosError": { 18 | "title": "Errore del server", 19 | "description": "Si è verificato un errore nella gestione della richiesta. Per favore, riprova.", 20 | "correlationIdText": "Se l'errore persiste, apri una richiesta all'assistenza. Inserisci le informazioni di contesto e il codice riportato sotto.", 21 | "tooltipTitle": "Identificativo copiato correttamente" 22 | }, 23 | "forbidden": { 24 | "title": "Non autorizzato", 25 | "description": "Non possiedi le autorizzazioni necessarie per visualizzare la pagina." 26 | }, 27 | "tokenExchange": { 28 | "title": "Errore di autenticazione", 29 | "description": "Per favore, torna ad Area Riservata e tenta nuovamente l'accesso" 30 | }, 31 | "assistencePartySelection": { 32 | "title": "Errore: non autorizzato", 33 | "description": "Non hai i permessi necessari per visualizzare PDND Interoperabilità in modalità assistenza, oppure la sessione è scaduta. Prova ad effettuare nuovamente l’accesso da Google Workspace." 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/static/locales/it/key.json: -------------------------------------------------------------------------------- 1 | { 2 | "backToKeyListBtn": "Torna alla lista delle chiavi", 3 | "edit": { 4 | "orphanAlertLabel": "L'utente che ha caricato questa chiave non fa più parte di questo client. Si consiglia di sostituire la chiave con una nuova. Per saperne di più, <1>leggi la guida.", 5 | "generalInformations": { 6 | "title": "Informazioni generali", 7 | "creationDateField": { 8 | "label": "Data di creazione" 9 | }, 10 | "uploaderField": { 11 | "label": "Caricata da" 12 | }, 13 | "kidField": { 14 | "label": "ID della chiave (kid)", 15 | "copySuccessFeedbackText": "ID copiato correttamente" 16 | }, 17 | "clientIdField": { 18 | "label": "ID del client (clientId)", 19 | "copySuccessFeedbackText": "ID copiato correttamente" 20 | }, 21 | "goToTechnicalDocButton": "Vai alla documentazione tecnica" 22 | } 23 | }, 24 | "list": { 25 | "title": "Chiavi pubbliche", 26 | "description": "Le chiavi pubbliche appartenenti a questo client compariranno nell’elenco.", 27 | "userEnableInfo": "Per poter caricare una chiave, devi prima aggiungerti come utente nella tab «Membri del client»", 28 | "publicKeyLimitInfo": "Hai raggiunto il numero massimo di chiavi per questo client", 29 | "supportDisableInfo": "L'utenza di assistenza non è abilitata ad aggiungere chiavi" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/static/locales/it/pagopa.json: -------------------------------------------------------------------------------- 1 | { 2 | "tos": { 3 | "title": "Interoperabilità", 4 | "description": "Accedendo, accetti i <1>Termini e condizioni d’uso del servizio e confermi di avere letto l’<2>Informativa Privacy.", 5 | "confirmBtnLabel": "Accedi" 6 | }, 7 | "footer": { 8 | "links": { 9 | "privacy": { 10 | "label": "Privacy Policy", 11 | "ariaLabel": "Vai al link: privacy policy" 12 | }, 13 | "dataProtection": { 14 | "label": "Diritto alla protezione dei dati personali", 15 | "ariaLabel": "Vai al link: diritto alla protezione dei dati personali" 16 | }, 17 | "terms": { 18 | "label": "Termini e condizioni", 19 | "ariaLabel": "Vai al link: termini e condizioni" 20 | }, 21 | "a11y": { 22 | "label": "Accessibilità", 23 | "ariaLabel": "Vai al link: accessibilità" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/static/locales/it/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "backToMemberListBtn": "Torna alla lista membri", 3 | "clientOperatorsTab": { 4 | "title": "Membri del client", 5 | "description": "I membri appartenenti a questo client compariranno nell’elenco. Se viene nominato un Admin API, ciascun membro potrà effettuare operazioni di scrittura." 6 | }, 7 | "actionReservedToAdminTooltip": "Quest’azione è riservata agli utenti con permessi da amministratore", 8 | "actions": { 9 | "removeFromClient": { 10 | "label": "Rimuovi dal client", 11 | "tooltip": "L’operazione è consentita solo agli utenti con permessi da amministratore" 12 | }, 13 | "delete": { 14 | "label": "Elimina", 15 | "tooltip": "L’operazione è consentita solo agli utenti con permessi da amministratore" 16 | } 17 | }, 18 | "edit": { 19 | "generalInformations": "Informazioni generali", 20 | "productRoleField": { 21 | "label": "Permessi su PDND Interoperabilità" 22 | }, 23 | "operatorKeysLink": { 24 | "label": "Vai alla vista delle chiavi associate all’utente" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/stores/dialog.store.ts: -------------------------------------------------------------------------------- 1 | import type { DialogProps } from '@/types/dialog.types' 2 | import { create } from 'zustand' 3 | 4 | type DialogStoreType = { 5 | dialog: DialogProps | null 6 | openDialog: (dialogState: DialogProps) => void 7 | closeDialog: () => void 8 | } 9 | 10 | export const useDialogStore = create((set) => ({ 11 | dialog: null, 12 | openDialog: (dialog: DialogProps) => set(() => ({ dialog })), 13 | closeDialog: () => set({ dialog: null }), 14 | })) 15 | 16 | export const useDialog = () => { 17 | const openDialog = useDialogStore((state) => state.openDialog) 18 | const closeDialog = useDialogStore((state) => state.closeDialog) 19 | 20 | return { openDialog, closeDialog } 21 | } 22 | -------------------------------------------------------------------------------- /src/stores/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dialog.store' 2 | export * from './loading-overlay.store' 3 | export * from './toast-notification.store' 4 | -------------------------------------------------------------------------------- /src/stores/loading-overlay.store.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand' 2 | 3 | type LoadingOverlayStoreType = { 4 | isShown: boolean 5 | message: string 6 | showOverlay: (message: string) => void 7 | hideOverlay: () => void 8 | } 9 | 10 | export const useLoadingOverlayStore = create((set) => ({ 11 | isShown: false, 12 | message: '', 13 | showOverlay: (message: string) => set(() => ({ message, isShown: true })), 14 | hideOverlay: () => set({ isShown: false }), 15 | })) 16 | 17 | export const useLoadingOverlay = () => { 18 | const showOverlay = useLoadingOverlayStore((state) => state.showOverlay) 19 | const hideOverlay = useLoadingOverlayStore((state) => state.hideOverlay) 20 | 21 | return { showOverlay, hideOverlay } 22 | } 23 | -------------------------------------------------------------------------------- /src/stores/toast-notification.store.ts: -------------------------------------------------------------------------------- 1 | import type React from 'react' 2 | import type { AlertProps } from '@mui/material' 3 | import { create } from 'zustand' 4 | 5 | type ToastNotificationStoreType = { 6 | isShown: boolean 7 | message: string | React.ReactNode 8 | severity: AlertProps['severity'] 9 | correlationId?: string 10 | showToast: ( 11 | message: string | React.ReactNode, 12 | severity: AlertProps['severity'], 13 | correlationId?: string 14 | ) => void 15 | hideToast: () => void 16 | } 17 | 18 | export const useToastNotificationStore = create((set) => ({ 19 | isShown: false, 20 | message: '', 21 | severity: 'success', 22 | showToast: ( 23 | message: string | React.ReactNode, 24 | severity: AlertProps['severity'], 25 | correlationId?: string 26 | ) => set(() => ({ message, severity, isShown: true, correlationId })), 27 | hideToast: () => set({ isShown: false }), 28 | })) 29 | 30 | export const useToastNotification = () => { 31 | const showToast = useToastNotificationStore((state) => state.showToast) 32 | const hideToast = useToastNotificationStore((state) => state.hideToast) 33 | 34 | return { showToast, hideToast } 35 | } 36 | -------------------------------------------------------------------------------- /src/types/attribute.types.ts: -------------------------------------------------------------------------------- 1 | import type { AttributeKind } from '@/api/api.generatedTypes' 2 | 3 | export type AttributeKey = Lowercase 4 | -------------------------------------------------------------------------------- /src/types/party.types.ts: -------------------------------------------------------------------------------- 1 | import type { ExternalId } from '@/api/api.generatedTypes' 2 | 3 | export type UserProductRole = 'admin' | 'security' | 'api' | 'support' 4 | 5 | export type DelegationType = 'DELEGATION_GRANTED' | 'DELEGATION_RECEIVED' 6 | 7 | type JwtOrg = { 8 | name: string 9 | roles: Array<{ 10 | role: UserProductRole 11 | }> 12 | fiscal_code: string 13 | } 14 | 15 | export type JwtUser = { 16 | aud: string 17 | exp: number 18 | iat: number 19 | iss: string 20 | jti: string 21 | nbf: number 22 | externalId?: ExternalId 23 | organization: JwtOrg 24 | selfcareId: string 25 | uid: string // the relationshipId between the user and the current institution 26 | name: string 27 | family_name: string 28 | organizationId: string 29 | rootParent?: { id: string; description: string } 30 | } 31 | -------------------------------------------------------------------------------- /src/types/risk-analysis-form.types.ts: -------------------------------------------------------------------------------- 1 | import type { FormConfigQuestion } from '@/api/api.generatedTypes' 2 | 3 | export type RiskAnalysisAnswerValue = string | Array | boolean 4 | export type RiskAnalysisQuestions = Record 5 | export type RiskAnalysisAnswers = Record 6 | export type RiskAnalysisKind = 'PA' | 'PRIVATE' 7 | -------------------------------------------------------------------------------- /src/utils/__tests__/eservice.utils.test.ts: -------------------------------------------------------------------------------- 1 | import { getDownloadDocumentName, getLastDescriptor } from '../eservice.utils' 2 | 3 | describe('getDownloadDocumentName utility function testing', () => { 4 | it('should correctly get the document namy from a DocumentRead data type', () => { 5 | const result = getDownloadDocumentName({ 6 | id: 'test-id', 7 | contentType: 'pdf', 8 | name: 'test.pdf', 9 | prettyName: 'document', 10 | checksum: 'f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2', 11 | }) 12 | 13 | expect(result).toEqual('document.pdf') 14 | }) 15 | }) 16 | 17 | describe('getLastDescriptor utility function testing', () => { 18 | it('should correctly get the last descriptor from an array of descriptors', () => { 19 | const result = getLastDescriptor([ 20 | { id: 'test-id-1', state: 'PUBLISHED', version: '1', audience: ['test-audience'] }, 21 | { id: 'test-id-2', state: 'PUBLISHED', version: '2', audience: ['test-audience'] }, 22 | { id: 'test-id-3', state: 'PUBLISHED', version: '3', audience: ['test-audience'] }, 23 | ]) 24 | 25 | expect(result?.id).toEqual('test-id-3') 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /src/utils/__tests__/tenant.utils.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | hasTenantGivenConsumerDelegationAvailability, 3 | hasTenantGivenProducerDelegationAvailability, 4 | isTenantCertifier, 5 | } from '../tenant.utils' 6 | 7 | const mockTenant = { 8 | id: 'test-id', 9 | name: 'test-name', 10 | externalId: { origin: 'test-origin', value: 'test-value' }, 11 | features: [ 12 | { 13 | certifier: { certifierId: 'test-certifierId' }, 14 | delegatedProducer: { availabilityTimestamp: 'test-timestamp' }, 15 | delegatedConsumer: { availabilityTimestamp: 'test-timestamp' }, 16 | }, 17 | ], 18 | createdAt: 'test-createdAt', 19 | attributes: { declared: [], verified: [], certified: [] }, 20 | } 21 | 22 | describe('isTenantCertifier utility function testing', () => { 23 | it('should correctly verify if tenant is certifier', () => { 24 | const result = isTenantCertifier(mockTenant) 25 | expect(result).toBe(true) 26 | }) 27 | }) 28 | 29 | describe('hasTenantGivenProducerDelegationAvailability utility function testing', () => { 30 | it('should correctly verify if tenant has given the producer delegations availability', () => { 31 | const result = hasTenantGivenProducerDelegationAvailability(mockTenant) 32 | expect(result).toBe(true) 33 | }) 34 | }) 35 | 36 | describe('hasTenantGivenProducerDelegationAvailability utility function testing', () => { 37 | it('should correctly verify if tenant has given the producer delegations availability', () => { 38 | const result = hasTenantGivenConsumerDelegationAvailability(mockTenant) 39 | expect(result).toBe(true) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /src/utils/array.utils.ts: -------------------------------------------------------------------------------- 1 | export const getKeys = Object.keys as >(obj: T) => Array 2 | -------------------------------------------------------------------------------- /src/utils/errors.utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This error is thrown when a query to the api returns a 404 status code. 3 | */ 4 | export class NotFoundError extends Error {} 5 | 6 | /** 7 | * This error is thrown when an api calls returns a 401 status code. 8 | */ 9 | export class UnauthorizedError extends Error {} 10 | 11 | /** 12 | * This error is thrown when the user is not authorized to view the requested resource. 13 | */ 14 | export class ForbiddenError extends Error {} 15 | 16 | /** 17 | * This error is thrown when there is an error in the token exchange process contained in the 18 | * `useAttemptLogin` hook. 19 | */ 20 | export class TokenExchangeError extends Error {} 21 | 22 | /** 23 | * This error is thrown when there is an error in the assistence party selection process. 24 | */ 25 | export class AssistencePartySelectionError extends Error {} 26 | -------------------------------------------------------------------------------- /src/utils/eservice.utils.ts: -------------------------------------------------------------------------------- 1 | import type { EServiceDoc, Document, CompactDescriptor } from '@/api/api.generatedTypes' 2 | 3 | export function getDownloadDocumentName(document: EServiceDoc | Document) { 4 | const filename: string = document.name 5 | const filenameBits: Array = filename.split('.').filter((b) => b) 6 | const fileExtension = filenameBits[filenameBits.length - 1] 7 | return `${document.prettyName}.${fileExtension}` 8 | } 9 | 10 | export function getLastDescriptor(descriptors: Array | undefined) { 11 | const descriptor = descriptors?.find((descriptor) => 12 | descriptors.every((d) => descriptor.version >= d.version) 13 | ) 14 | return descriptor 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/format.utils.ts: -------------------------------------------------------------------------------- 1 | const numFormatter = new Intl.NumberFormat('it-IT') 2 | export function formatThousands(num: number) { 3 | return numFormatter.format(num) 4 | } 5 | 6 | const dateFormatter = new Intl.DateTimeFormat('it', { 7 | day: '2-digit', 8 | month: 'long', 9 | year: 'numeric', 10 | }) 11 | export function formatDateString(dateString: string) { 12 | return dateFormatter.format(new Date(dateString)) 13 | } 14 | 15 | export function secondsToHoursMinutes(totalSeconds: number) { 16 | const totalMinutes = totalSeconds / 60 17 | const hours = Math.floor(totalMinutes / 60) 18 | const minutes = totalMinutes % 60 19 | return { hours, minutes } 20 | } 21 | 22 | export function minutesToSeconds(minutes: number) { 23 | return minutes * 60 24 | } 25 | 26 | export function secondsToMinutes(seconds: number) { 27 | return seconds / 60 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/tenant.utils.ts: -------------------------------------------------------------------------------- 1 | import type { Tenant, TenantFeature } from '@/api/api.generatedTypes' 2 | 3 | export function isTenantCertifier(tenant: Tenant) { 4 | return tenant.features.some((feature) => 'certifier' in feature && feature.certifier?.certifierId) 5 | } 6 | 7 | export function hasTenantGivenProducerDelegationAvailability(tenant: Tenant) { 8 | return Boolean( 9 | tenant.features.find( 10 | (feature): feature is Extract => 11 | Boolean('delegatedProducer' in feature && feature.delegatedProducer?.availabilityTimestamp) 12 | )?.delegatedProducer?.availabilityTimestamp 13 | ) 14 | } 15 | 16 | export function hasTenantGivenConsumerDelegationAvailability(tenant: Tenant) { 17 | return Boolean( 18 | tenant.features.find( 19 | (feature): feature is Extract => 20 | Boolean('delegatedConsumer' in feature && feature.delegatedConsumer?.availabilityTimestamp) 21 | )?.delegatedConsumer?.availabilityTimestamp 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "module": "ESNext", 12 | "moduleResolution": "Node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | "baseUrl": ".", 18 | "paths": { 19 | "@/*": ["src/*"], 20 | }, 21 | "types": ["vitest/globals"], 22 | }, 23 | "include": ["src", "./setupTests.ts"], 24 | "exclude": [ 25 | // "./src/**/__tests__/**", 26 | "__mocks__", 27 | ], 28 | "references": [{ "path": "./tsconfig.node.json" }] 29 | } 30 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | --------------------------------------------------------------------------------