Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
98 changes: 98 additions & 0 deletions __mocks__/data/purpose.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,65 @@
rulesetExpiration: '2030-01-01T00:00:00Z',
})

const createMockPurposeCallsExceed = createMockFactory<Purpose>({
id: 'purpose-id',
title: 'Test Purpose',
consumer: { id: 'consumer-id', name: 'Consumer Name' },
eservice: {
id: 'eservice-id',
name: 'Test Eservice',
mode: 'DELIVER',
producer: { id: 'producer-id', name: 'Producer Name' },
personalData: false,
descriptor: {
id: 'descriptor-id',
state: 'PUBLISHED',
version: '1',
audience: ['test'],
},
},
agreement: { id: 'agreement-id', state: 'ACTIVE', canBeUpgraded: false },

Check failure on line 218 in __mocks__/data/purpose.mocks.ts

View workflow job for this annotation

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

Property 'consumerId' is missing in type '{ id: string; state: "ACTIVE"; canBeUpgraded: false; }' but required in type 'CompactAgreement'.
riskAnalysisForm: {
answers: { usesPersonalData: ['YES'] },
version: '3.1',
riskAnalysisId: 'risk-analysis-id',
},
versions: [
{
createdAt: '2023-02-03T07:59:52.458Z',
dailyCalls: 1,
firstActivationAt: '2023-02-03T08:26:43.139Z',
id: '3a5c9422-876c-4de8-828a-66586fd68b55',
riskAnalysisDocument: {
contentType: 'application/pdf',
createdAt: '2023-02-03T08:26:43.049Z',
id: '3562b028-0193-45fa-acf9-4bbe1ced352a',
},
state: 'ACTIVE',
},
],
clients: [],
description: '',
isFreeOfCharge: false,
dailyCallsPerConsumer: 0,
currentVersion: {
createdAt: '2023-02-03T07:59:52.458Z',
dailyCalls: 1,
firstActivationAt: '2023-02-03T08:26:43.139Z',
id: '3a5c9422-876c-4de8-828a-66586fd68b55',
riskAnalysisDocument: {
contentType: 'application/pdf',
createdAt: '2023-02-03T08:26:43.049Z',
id: '3562b028-0193-45fa-acf9-4bbe1ced352a',
},
state: 'ACTIVE',
},
dailyCallsTotal: 0,
hasUnreadNotifications: false,
isDocumentReady: false,
rulesetExpiration: undefined,
})

const createMockPurposeCompatiblePersonalDataYes = createMockFactory<Purpose>({
...createMockPurposeUsesPersonalDataAnswerNo(),
riskAnalysisForm: {
Expand All @@ -214,11 +273,50 @@
},
})

const createMockPurposeCallsPerConsumerExceed = createMockFactory<Purpose>({
...createMockPurposeCallsExceed(),
currentVersion: {
id: '1',
state: 'ACTIVE',
createdAt: '2023-02-03T07:59:52.458Z',
dailyCalls: 2,
},
dailyCallsPerConsumer: 1,
dailyCallsTotal: 10,
})

const createMockPurposeCallsTotalExceed = createMockFactory<Purpose>({
...createMockPurposeCallsExceed(),
currentVersion: {
id: '1',
state: 'ACTIVE',
createdAt: '2023-02-03T07:59:52.458Z',
dailyCalls: 2,
},
dailyCallsPerConsumer: 10,
dailyCallsTotal: 1,
})

const createMockPurposeCallsWithoutExceed = createMockFactory<Purpose>({
...createMockPurposeCallsExceed(),
currentVersion: {
id: '1',
state: 'ACTIVE',
createdAt: '2023-02-03T07:59:52.458Z',
dailyCalls: 1,
},
dailyCallsPerConsumer: 10,
dailyCallsTotal: 100,
})

export {
createMockPurpose,
createMockRiskAnalysisFormConfig,
createMockPurposeUsesPersonalDataAnswerNo,
createMockPurposeUsesPersonalDataAnswerYes,
createMockPurposeCompatiblePersonalDataYes,
createMockPurposeCompatiblePersonalDataNo,
createMockPurposeCallsPerConsumerExceed,
createMockPurposeCallsTotalExceed,
createMockPurposeCallsWithoutExceed,
}
36 changes: 36 additions & 0 deletions src/components/shared/DelegationTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { type DelegationWithCompactTenants } from '@/api/api.generatedTypes'
import { AuthHooks } from '@/api/auth'
import { AltRoute } from '@mui/icons-material'
import { Tooltip } from '@mui/material'
import React from 'react'
import { useTranslation } from 'react-i18next'

type DelegationTooltipProps = {
delegation: DelegationWithCompactTenants
}

export const DelegationTooltip: React.FC<DelegationTooltipProps> = ({ delegation }) => {
const { t } = useTranslation('shared-components', { keyPrefix: 'delegationTooltip' })
const { jwt } = AuthHooks.useJwt()

const isDelegator = Boolean(delegation.delegator.id === jwt?.organizationId)
const delegator = delegation.delegator.name
const delegate = delegation.delegate.name

const label = isDelegator
? t('label.delegator', { delegate })
: t('label.delegate', { delegator })

return (
<Tooltip title={label}>
<AltRoute
color="primary"
sx={{
rotate: '90deg',
height: 18,
width: 18,
}}
/>
</Tooltip>
)
}
14 changes: 14 additions & 0 deletions src/components/shared/GreyAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Alert } from '@mui/material'
import React from 'react'

interface GreyAlertProps {
children: React.ReactNode
}

export const GreyAlert: React.FC<GreyAlertProps> = ({ children }) => {
return (
<Alert icon={false} sx={{ p: 2, borderLeftColor: 'grey.700', backgroundColor: 'grey.50' }}>
{children}
</Alert>
)
}
4 changes: 2 additions & 2 deletions src/components/shared/SummaryAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ export const SummaryAccordion: React.FC<SummaryAccordionProps> = ({
},
}}
>
<Box>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="subtitle2">{headline}</Typography>
<Typography sx={{ mt: 2 }} variant="h6">
<Typography sx={{ ml: 1 }} variant="h6">
{title}
</Typography>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export function _RHFAutocompleteBase<
<TextField
variant={variant}
error={!!error}
required={Boolean(rules?.required)}
placeholder={placeholder ?? '...'}
{...params}
autoFocus={focusOnMount}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PageContainer } from '@/components/layout/containers'
import { useTranslation } from 'react-i18next'
import { PurposeCreateForm } from './components/PurposeCreateForm'
import { useParams } from '@/router'
import { Typography } from '@mui/material'

const ConsumerPurposeCreatePage: React.FC = () => {
const { t } = useTranslation('purpose')
Expand All @@ -16,6 +17,15 @@ const ConsumerPurposeCreatePage: React.FC = () => {
to: 'SUBSCRIBE_PURPOSE_LIST',
}}
>
<Typography
sx={{
fontSize: 16,
fontWeight: 700,
color: 'text.secondary',
}}
>
{t('create.requiredLabel')}
</Typography>
<PurposeCreateForm purposeTemplateId={purposeTemplateId} />
</PageContainer>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, it, expect, vi } from 'vitest'
import { screen } from '@testing-library/react'
import ConsumerPurposeCreatePage from '../ConsumerPurposeCreate.page'
import { mockUseJwt, renderWithApplicationContext } from '@/utils/testing.utils'

mockUseJwt()
const useQueryMock = vi.fn()

describe('ConsumerPurposeCreatePage', () => {
it('renders page title', () => {
useQueryMock.mockReturnValue({
isLoading: false,
})

renderWithApplicationContext(<ConsumerPurposeCreatePage />, {
withReactQueryContext: true,
withRouterContext: true,
})

expect(screen.getByText('create.emptyTitle')).toBeInTheDocument()
})
it('renders required label', () => {
useQueryMock.mockReturnValue({
isLoading: false,
})

renderWithApplicationContext(<ConsumerPurposeCreatePage />, {
withReactQueryContext: true,
withRouterContext: true,
})

expect(screen.getByText('create.requiredLabel')).toBeInTheDocument()
})
})
10 changes: 10 additions & 0 deletions src/pages/ConsumerPurposeEditPage/ConsumerPurposeEdit.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { PurposeEditStepRiskAnalysis } from './components/PurposeEditStepRiskAna
import { useParams, useNavigate } from '@/router'
import { PurposeQueries } from '@/api/purpose'
import { useQuery } from '@tanstack/react-query'
import { Typography } from '@mui/material'

const ConsumerPurposeEditPage: React.FC = () => {
const { t } = useTranslation('purpose')
Expand Down Expand Up @@ -60,6 +61,15 @@ const ConsumerPurposeEditPage: React.FC = () => {
to: 'SUBSCRIBE_PURPOSE_LIST',
}}
>
<Typography
sx={{
fontSize: 16,
fontWeight: 700,
color: 'text.secondary',
}}
>
{t('create.requiredLabel')}
</Typography>
{!isReceive && <Stepper steps={steps} activeIndex={activeStep} />}
<Step {...stepProps} />
</PageContainer>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { Box } from '@mui/material'
import { Alert, AlertTitle, Box, Stack, Typography } from '@mui/material'
import { FormProvider, useForm } from 'react-hook-form'
import { RHFRadioGroup, RHFTextField } from '@/components/shared/react-hook-form-inputs'
import { useTranslation } from 'react-i18next'
Expand All @@ -10,6 +10,8 @@ import type { ActiveStepProps } from '@/hooks/useActiveStep'
import type { Purpose, PurposeUpdateContent } from '@/api/api.generatedTypes'
import SaveIcon from '@mui/icons-material/Save'
import { useNavigate } from '@/router'
import { useGetConsumerPurposeEditPageInfoAlertProps } from '../../hooks/useGetConsumerPurposeEditPageInfoAlertProps'
import { GreyAlert } from '@/components/shared/GreyAlert'

export type PurposeEditStepGeneralFormValues = Omit<
PurposeUpdateContent,
Expand Down Expand Up @@ -75,6 +77,18 @@ const PurposeEditStepGeneralForm: React.FC<PurposeEditStepGeneralFormProps> = ({

const isFreeOfCharge = formMethods.watch('isFreeOfCharge')

const dailyCallsFormValue = formMethods.watch('dailyCalls')

const dailyCallsPerConsumer = purpose.dailyCallsPerConsumer

const dailyCallsTotal = purpose.dailyCallsTotal

const alertProps = useGetConsumerPurposeEditPageInfoAlertProps(
dailyCallsFormValue,
dailyCallsPerConsumer,
dailyCallsTotal
)

return (
<FormProvider {...formMethods}>
<Box component="form" noValidate onSubmit={formMethods.handleSubmit(onSubmit)}>
Expand All @@ -86,6 +100,7 @@ const PurposeEditStepGeneralForm: React.FC<PurposeEditStepGeneralFormProps> = ({
focusOnMount
inputProps={{ maxLength: 60 }}
rules={{ required: true, minLength: 5 }}
required
/>

<RHFTextField
Expand All @@ -95,6 +110,7 @@ const PurposeEditStepGeneralForm: React.FC<PurposeEditStepGeneralFormProps> = ({
multiline
inputProps={{ maxLength: 250 }}
rules={{ required: true, minLength: 10 }}
required
/>

<RHFRadioGroup
Expand All @@ -116,15 +132,57 @@ const PurposeEditStepGeneralForm: React.FC<PurposeEditStepGeneralFormProps> = ({
rules={{ required: true, minLength: 10 }}
/>
)}

</SectionContainer>
<SectionContainer
title={t('edit.loadEstimationSection.title')}
description={t('edit.loadEstimationSection.description')}
>
<RHFTextField
name="dailyCalls"
label={t('edit.stepGeneral.dailyCallsField.label')}
label={t('edit.loadEstimationSection.dailyCalls.label')}
infoLabel={t('edit.loadEstimationSection.dailyCalls.infoLabel')}
type="number"
inputProps={{ min: '1' }}
sx={{ mb: 0 }}
rules={{ required: true, min: 1 }}
/>
{alertProps && <Alert {...alertProps} sx={{ mt: 1, mb: 3 }} />}
<GreyAlert>
<AlertTitle sx={{ textTransform: 'uppercase', fontWeight: 700 }}>
{t('edit.loadEstimationSection.providerThresholdsInfo.label')}
</AlertTitle>
<Stack direction="row" spacing={6} sx={{ mt: 0.5, mb: 1 }}>
<Stack direction="row" spacing={2} alignItems="center">
<Typography>
{t(
'edit.loadEstimationSection.providerThresholdsInfo.dailyCallsPerConsumer.label'
)}
</Typography>
<Typography fontWeight={600}>
{t(
'edit.loadEstimationSection.providerThresholdsInfo.dailyCallsPerConsumer.value',
{
min: '#' /* @TODO - add residual threshold */,
max: dailyCallsPerConsumer,
}
)}
</Typography>
</Stack>
<Stack direction="row" spacing={2} alignItems="center">
<Typography>
{t('edit.loadEstimationSection.providerThresholdsInfo.dailyCallsTotal.label')}
</Typography>
<Typography fontWeight={600}>
{t('edit.loadEstimationSection.providerThresholdsInfo.dailyCallsTotal.value', {
min: '#' /* @TODO - add residual threshold */,
max: dailyCallsTotal,
})}
</Typography>
</Stack>
</Stack>
<Typography variant="caption" color="text.secondary">
{t('edit.loadEstimationSection.providerThresholdsInfo.description')}
</Typography>
</GreyAlert>
</SectionContainer>
<StepActions
back={{ to: 'SUBSCRIBE_PURPOSE_LIST', label: t('backToListBtn'), type: 'link' }}
Expand Down
Loading
Loading