Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
d0ef63a
feat: added NotificationConfig page
borgesis95 Jul 23, 2025
39e5c04
feat: added several components
borgesis95 Jul 23, 2025
2bf47da
feat: start to add mutation
borgesis95 Jul 23, 2025
32ef19e
chore: added message icon
borgesis95 Aug 26, 2025
9a292f0
chore: added SwithcLabelDescription component
borgesis95 Aug 26, 2025
e2f7679
chore: update NotificationConfigSection
borgesis95 Aug 27, 2025
99bd626
feat: added translation
borgesis95 Aug 27, 2025
8951811
chore: association switch - configuration
borgesis95 Aug 28, 2025
b970629
chore: added services and queries
borgesis95 Aug 28, 2025
6628007
chore: added defaultValue retrieving from get (mocked) api
borgesis95 Sep 2, 2025
17f12e5
refactor: removed var inAppConfig from hook
borgesis95 Sep 2, 2025
af2f2bf
chore: add debounce
borgesis95 Sep 3, 2025
decc7b8
chore: added handling of "enable all" for section
borgesis95 Sep 3, 2025
9aa02f5
chore: changdd switch "enable all" with button
borgesis95 Sep 4, 2025
5121611
chore: added visibilty field (in-app)
borgesis95 Sep 8, 2025
9fe01f7
refactor: moved types into specific file
borgesis95 Sep 8, 2025
b77bb1c
test: added NotificationUserConfigPage test
borgesis95 Sep 10, 2025
1b56f79
test: added NotificationUserConfigPage test (II)
borgesis95 Sep 10, 2025
47bb221
test: fix useGetSidebarItems.test.tsx
borgesis95 Sep 10, 2025
3cf662e
test: InAppNotificationUserConfigTab
borgesis95 Sep 10, 2025
1854129
test: fix InAppNotificationUserConfigTab
borgesis95 Sep 10, 2025
7d59414
temp: fix on notification.services
borgesis95 Sep 10, 2025
829dde9
test: EmailNotificationUserconfigTab
borgesis95 Sep 10, 2025
751daff
test: add formProvider
borgesis95 Sep 11, 2025
c93e8d7
chore: update NotificationUserConfigTab
borgesis95 Sep 18, 2025
07a6b9a
refactor: added some logic within usenNotificationConfigHook
borgesis95 Sep 18, 2025
7275982
chore: added info for "digest"
borgesis95 Sep 18, 2025
d860bf4
feat: added tenant notification configuration
borgesis95 Sep 18, 2025
45865ce
chore: added outcome snackbar when saving new configuration
borgesis95 Sep 19, 2025
6ee3b5d
chore: added information about tenant config on UpdatePartyMailDrawer
borgesis95 Sep 19, 2025
b80affa
test: added several test
borgesis95 Sep 23, 2025
fec6f34
chore: UpdatePartyMailDrawer new config
borgesis95 Sep 23, 2025
774e2fe
fix: resolve sonarQube reliability
borgesis95 Sep 23, 2025
04be812
chore: added inAppNotificationPrefence
borgesis95 Sep 29, 2025
381fc5b
chore: removed state to handle emailPreference
borgesis95 Sep 29, 2025
3a6b1fc
refactor: added header for email and inapp config
borgesis95 Sep 29, 2025
6112a67
test: fix test
borgesis95 Sep 29, 2025
669cd88
chore: add userEmail on jwt decoded data
borgesis95 Oct 10, 2025
ab85470
chore: added feature flag notification config
borgesis95 Oct 10, 2025
478e4ac
chore: update useGetSidebarItems with ff notification
borgesis95 Oct 10, 2025
538b4f1
chore: added AlertInfo
borgesis95 Oct 10, 2025
844ebc0
chore: removed unnecessary text
borgesis95 Oct 10, 2025
71ce52c
css: addes several margin around config page
borgesis95 Oct 14, 2025
9be4181
fix notification config for clientKeyAddedDeletedToClientUsers param …
GorlemZ Nov 28, 2025
83815df
Merge branch 'dev' into PIN-7318_configuration_notifications_page
borgesis95 Dec 2, 2025
65fae1e
chore: added new management of digest (mail)
borgesis95 Dec 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ window.pagopa_env = {
'https://selfcare.dev.interop.pagopa.it/m2m/v2-interface-specification.yaml',
SIGNALHUB_PERSONAL_DATA_PROCESS_URL: 'http://localhost',
ERROR_DATA_DURATION_TIME: '90000',
FEATURE_FLAG_NOTIFICATION_CONFIG: 'true',
FEATURE_FLAG_ESERVICE_PERSONAL_DATA: 'true',
FEATURE_FLAG_USE_SIGNED_DOCUMENT: 'false',
}
2 changes: 2 additions & 0 deletions setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { afterEach, vi } from 'vitest'
import { cleanup } from '@testing-library/react'
// extends Vitest's expect method with methods from react-testing-library
import '@testing-library/jest-dom/vitest'
import { FEATURE_FLAG_NOTIFICATION_CONFIG } from '@/config/env'

const localhost = 'http://localhost:3000/0.0'
export const testConfigs = {
Expand Down Expand Up @@ -30,6 +31,7 @@ export const testConfigs = {
API_GATEWAY_V2_INTERFACE_URL:
'https://selfcare.dev.interop.pagopa.it/m2m/v2-interface-specification.yaml',
SIGNALHUB_PERSONAL_DATA_PROCESS_URL: 'http://localhost',
FEATURE_FLAG_NOTIFICATION_CONFIG: 'true',
FEATURE_FLAG_ESERVICE_PERSONAL_DATA: 'true',
FEATURE_FLAG_USE_SIGNED_DOCUMENT: 'true',
}
Expand Down
7 changes: 7 additions & 0 deletions src/api/api.generatedTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ export interface EServiceTemplateRiskAnalysis {
tenantKind: TenantKind
/** @format date-time */
createdAt: string
/** @format date-time */
rulesetExpiration?: string
}

export interface ProducerEServiceDescriptor {
Expand Down Expand Up @@ -627,6 +629,7 @@ export interface CatalogEService {
activeDescriptor?: CompactDescriptor
/** Indicates if there are unread notifications for this e-service */
hasUnreadNotifications?: boolean
personalData?: boolean
}

export type ClientKind = 'API' | 'CONSUMER'
Expand Down Expand Up @@ -2347,6 +2350,8 @@ export interface NotificationConfig {
delegationSubmittedRevokedToDelegate: boolean
certifiedVerifiedAttributeAssignedRevokedToAssignee: boolean
clientKeyAndProducerKeychainKeyAddedDeletedToClientUsers: boolean
purposeQuotaAdjustmentRequestToProducer: boolean
purposeOverQuotaStateToConsumer: boolean
}

export interface TenantNotificationConfig {
Expand Down Expand Up @@ -3238,6 +3243,7 @@ export interface IsEServiceNameAvailableParams {
export interface GetNotificationsParams {
/** Query to filter notifications */
q?: string
unread?: boolean
/** Category to filter notifications */
category?: 'Subscribers' | 'Providers' | 'Delegations' | 'AttributesAndKeys'
/**
Expand Down Expand Up @@ -8519,6 +8525,7 @@ export namespace InAppNotifications {
export type RequestQuery = {
/** Query to filter notifications */
q?: string
unread?: boolean
/** Category to filter notifications */
category?: 'Subscribers' | 'Providers' | 'Delegations' | 'AttributesAndKeys'
/**
Expand Down
4 changes: 3 additions & 1 deletion src/api/auth/auth.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
export const parseJwt = memoize((token: string | null | undefined) => {
const jwt = token ? jwtDecode<JwtUser>(token) : undefined
const currentRoles = jwt ? jwt.organization.roles.map((r) => r.role) : []
const isAdmin = currentRoles.length === 1 && currentRoles[0] === 'admin'
const isAdmin = currentRoles.length === 1 && currentRoles[0] === ''

Check failure on line 14 in src/api/auth/auth.utils.ts

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest, 18.15.0)

This comparison appears to be unintentional because the types 'UserProductRole' and '""' have no overlap.
const isOperatorAPI = currentRoles.includes('api')
const isOperatorSecurity = currentRoles.includes('security')
const isSupport = currentRoles.includes('support')
const isOrganizationAllowedToProduce = !!(
jwt?.externalId && PRODUCER_ALLOWED_ORIGINS.includes(jwt.externalId.origin)
)
const userEmail = jwt?.email

return {
jwt,
Expand All @@ -27,5 +28,6 @@
isOperatorSecurity,
isSupport,
isOrganizationAllowedToProduce,
userEmail,
}
})
3 changes: 3 additions & 0 deletions src/api/notification/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './notification.services'
export * from './notification.mutations'
export * from './notification.queries'
26 changes: 26 additions & 0 deletions src/api/notification/notification.mutations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useMutation } from '@tanstack/react-query'
import { NotificationServices } from './index'
import { useTranslation } from 'react-i18next'
function useUpdateNotificationUserConfigs() {
const { t } = useTranslation('notification', {
keyPrefix: 'configurationPage.outcome',
})
return useMutation({
mutationFn: NotificationServices.updateUserNotificationConfigs,
meta: {
successToastLabel: t('success'),
errorToastLabel: t('error'),
},
})
}

function useUpdateNotificationTenantConfigs() {
return useMutation({
mutationFn: NotificationServices.updateTenantNotificationConfigs,
})
}

export const NotificationMutations = {
useUpdateNotificationUserConfigs,
useUpdateNotificationTenantConfigs,
}
21 changes: 21 additions & 0 deletions src/api/notification/notification.queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { queryOptions } from '@tanstack/react-query'
import { NotificationServices } from './notification.services'

function getUserNotificationConfigs() {
return queryOptions({
queryKey: ['getUserNotificationConfiguration'],
queryFn: () => NotificationServices.getUserNotificationConfigs(),
})
}

function getTenantNotificationConfigs() {
return queryOptions({
queryKey: ['getTenantNotificationConfiguration'],
queryFn: () => NotificationServices.getTenantNotificationConfigs(),
})
}

export const NotificationQueries = {
getUserNotificationConfigs,
getTenantNotificationConfigs,
}
42 changes: 42 additions & 0 deletions src/api/notification/notification.services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import axiosInstance from '@/config/axios'
import { BACKEND_FOR_FRONTEND_URL } from '@/config/env'
import type {
TenantNotificationConfig,
TenantNotificationConfigUpdateSeed,
UserNotificationConfig,
UserNotificationConfigUpdateSeed,
} from '../api.generatedTypes'

async function updateUserNotificationConfigs(payload: UserNotificationConfigUpdateSeed) {
return await axiosInstance.post<void>(
`${BACKEND_FOR_FRONTEND_URL}/userNotificationConfigs`,
payload
)
}

async function updateTenantNotificationConfigs(payload: TenantNotificationConfigUpdateSeed) {
return await axiosInstance.post<void>(
`${BACKEND_FOR_FRONTEND_URL}/tenantNotificationConfigs`,
payload
)
}

async function getUserNotificationConfigs() {
const response = await axiosInstance.get<UserNotificationConfig>(
`${BACKEND_FOR_FRONTEND_URL}/userNotificationConfigs`
)

return response.data
}
async function getTenantNotificationConfigs() {
const response = await axiosInstance.get<TenantNotificationConfig>(
`${BACKEND_FOR_FRONTEND_URL}/tenantNotificationConfigs`
)
return response.data
}
export const NotificationServices = {
updateUserNotificationConfigs,
updateTenantNotificationConfigs,
getUserNotificationConfigs,
getTenantNotificationConfigs,
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next'
import { getAriaAccessibilityInputProps, mapValidationErrorMessages } from '@/utils/form.utils'

export type RHFSwitchProps = Omit<MUISwitchProps, 'checked' | 'onChange'> & {
label: string
label: string | React.ReactNode
infoLabel?: string
name: string
rules?: ControllerProps['rules']
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Typography } from '@mui/material'
import { Box } from '@mui/system'

type SwitchLabelDescriptionProps = {
label: string
description: string
}

export const SwitchLabelDescription: React.FC<SwitchLabelDescriptionProps> = ({
label,
description,
}) => {
return (
<Box component="div" display="flex" flexDirection="column">
<Typography component="p" variant="body2" fontWeight={400}>
{label}
</Typography>
<Typography component="p" color="text.secondary" variant="caption">
{description}
</Typography>
</Box>
)
}
1 change: 1 addition & 0 deletions src/components/shared/react-hook-form-inputs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export * from './RHFRadioGroup'
export * from './RHFCheckboxGroup'
export * from './RHFSingleFileInput'
export * from './RHFCheckbox'
export * from './SwitchLabelDescription'
export * from './RHFSelect'
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ describe('useGetSidebarItems', () => {
mockUseGetActiveUserParty()
const { result } = renderHook(() => useGetSidebarItems())

expect(result.current.length).toBe(6)
expect(result.current.length).toBe(7)
})
})
11 changes: 10 additions & 1 deletion src/components/sidebar/useGetSidebarItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import type { RouteKey } from '@/router'
import { routes } from '@/router'
import DnsIcon from '@mui/icons-material/Dns'
import { ConsumerIcon, ProviderIcon, CatalogIcon, DeveloperToolIcon, MyTenantIcon } from '@/icons'
import NotificationsIcon from '@mui/icons-material/Notifications'
import { useTranslation } from 'react-i18next'
import { FEATURE_FLAG_NOTIFICATION_CONFIG } from '@/config/env'

export function useGetSidebarItems(): SidebarRoutes {
const { t } = useTranslation('sidebar', { keyPrefix: 'menuItem' })
Expand All @@ -23,7 +25,14 @@ export function useGetSidebarItems(): SidebarRoutes {
icon: CatalogIcon,
label: t('eserviceCatalog'),
children: [],
},
{
icon: NotificationsIcon,
rootRouteKey: 'NOTIFICATIONS',
label: 'Notifiche ',
children: [],
divider: true,
hide: !FEATURE_FLAG_NOTIFICATION_CONFIG,
},
{
showNotification: false,
Expand Down Expand Up @@ -89,7 +98,7 @@ export function useGetSidebarItems(): SidebarRoutes {
{
icon: DeveloperToolIcon,
rootRouteKey: 'DEVELOPER_TOOLS',
label: 'Tool per lo sviluppo ',
label: 'Tool per lo sviluppo',
children: [],
},
]
Expand Down
2 changes: 2 additions & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const GeneralConfigs = z.object({
const FeatureFlagConfigs = z.object({
FEATURE_FLAG_ADMIN_CLIENT: z.enum(['true', 'false']),
FEATURE_FLAG_AGREEMENT_APPROVAL_POLICY_UPDATE: z.enum(['true', 'false']),
FEATURE_FLAG_NOTIFICATION_CONFIG: z.enum(['true', 'false']),
FEATURE_FLAG_ESERVICE_PERSONAL_DATA: z
.enum(['true', 'false'])
.default('false')
Expand Down Expand Up @@ -111,6 +112,7 @@ export const {
API_GATEWAY_V1_INTERFACE_URL,
API_GATEWAY_V2_INTERFACE_URL,
ERROR_DATA_DURATION_TIME,
FEATURE_FLAG_NOTIFICATION_CONFIG,
FEATURE_FLAG_ESERVICE_PERSONAL_DATA,
FEATURE_FLAG_USE_SIGNED_DOCUMENT,
} = parseConfigs()
Expand Down
4 changes: 4 additions & 0 deletions src/config/react-i18next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import assistanceEnNs from '@/static/locales/en/assistance.json'
import keychainEnNs from '@/static/locales/en/keychain.json'
import eserviceTemplateEnNs from '@/static/locales/en/eserviceTemplate.json'
import developerToolsEnNs from '@/static/locales/en/developer-tools.json'
import notificationEnNs from '@/static/locales/en/notification.json'
import purposeTemplateEnNs from '@/static/locales/en/purposeTemplate.json'

import pagesItNs from '@/static/locales/it/pages.json'
Expand All @@ -43,6 +44,7 @@ import keychainItNs from '@/static/locales/it/keychain.json'
import eserviceTemplateItNs from '@/static/locales/it/eserviceTemplate.json'
import developerToolsItNs from '@/static/locales/it/developer-tools.json'
import sidebarNs from '@/static/locales/it/sidebar.json'
import notificationItNs from '@/static/locales/it/notification.json'
import purposeTemplateItNs from '@/static/locales/it/purposeTemplate.json'

i18n.use(initReactI18next).init({
Expand Down Expand Up @@ -74,6 +76,7 @@ i18n.use(initReactI18next).init({
eserviceTemplate: eserviceTemplateItNs,
'developer-tools': developerToolsItNs,
sidebar: sidebarNs,
notification: notificationItNs,
purposeTemplate: purposeTemplateItNs,
},
en: {
Expand All @@ -97,6 +100,7 @@ i18n.use(initReactI18next).init({
eserviceTemplate: eserviceTemplateEnNs,
'developer-tools': developerToolsEnNs,
sidebar: sidebarNs,
notification: notificationEnNs,
purposeTemplate: purposeTemplateEnNs,
},
},
Expand Down
13 changes: 13 additions & 0 deletions src/icons/MessageIcon/MessageIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react'
import { SvgIcon } from '@mui/material'

export const MessageIcon = () => (
<SvgIcon>
<path
d="M20 2H4C2.9 2 2.01 2.9 2.01 4L2 22L6 18H20C21.1 18 22 17.1 22 16V4C22 2.9 21.1 2 20 2ZM18 14H6V12H18V14ZM18 11H6V9H18V11ZM18 8H6V6H18V8Z"
fill="#17324D"
/>
</SvgIcon>
)

MessageIcon.muiName = 'MessageIcon'
1 change: 1 addition & 0 deletions src/icons/MessageIcon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './MessageIcon'
1 change: 1 addition & 0 deletions src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './ProviderIcon'
export * from './MyTenantIcon'
export * from './CatalogIcon'
export * from './DeveloperToolIcon'
export * from './MessageIcon'
Loading
Loading