Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/single rights/503 choose rights page #518

Merged
merged 31 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b00d957
first implementation of ChooseRightsPage
Albertlarsen Aug 24, 2023
8f45e7a
Change status check
Albertlarsen Aug 24, 2023
c7c9bad
implementation of warning color on ActionBars
Albertlarsen Aug 25, 2023
74c81f2
Merge branch 'main' into feat/single-rights/503-ChooseRightsPage
Albertlarsen Aug 25, 2023
62054f3
Before fixing rendered fewer hooks than expected
Albertlarsen Aug 28, 2023
569b63c
Before fixing rendered fewer hooks than expected
Albertlarsen Aug 28, 2023
c77eee6
remove function working
Albertlarsen Aug 29, 2023
41b536f
not-checked-by-default
Albertlarsen Aug 29, 2023
1827417
not-checked-by-default-2
Albertlarsen Aug 29, 2023
31ed051
Finish ChooseRightsPage
Albertlarsen Aug 31, 2023
94b18f0
make buttons disabled
Albertlarsen Aug 31, 2023
0a81746
Merge branch 'main' into feat/single-rights/503-ChooseRightsPage
Albertlarsen Aug 31, 2023
1385087
Code cleanup
Albertlarsen Aug 31, 2023
635d352
add useMemo
Albertlarsen Aug 31, 2023
3abb152
Code cleanup
Albertlarsen Aug 31, 2023
9ddbf8e
update fds tokens
Albertlarsen Aug 31, 2023
d033a65
PR Code improvement
Albertlarsen Sep 1, 2023
dbd12a9
Separate RightsActionBar from ResourceActionBar (#521)
allinox Sep 4, 2023
c2a59df
Make first ActionBar open
Albertlarsen Sep 4, 2023
77301f2
before trying to fix checked chips logic
Albertlarsen Sep 4, 2023
f0d67d8
First part fixing checked toggle state
Albertlarsen Sep 4, 2023
cba3db9
Fixed checked states not working
Albertlarsen Sep 4, 2023
87bb32b
Fix navigationButton placement
Albertlarsen Sep 4, 2023
3249a16
Fix navigation buttons placement
Albertlarsen Sep 4, 2023
4aa3d5f
Filter NotDelegable chips
Albertlarsen Sep 4, 2023
a6bd79b
Merge branch 'main' into feat/single-rights/503-ChooseRightsPage
Albertlarsen Sep 5, 2023
5458750
use moved textCode function
Albertlarsen Sep 5, 2023
687ac1c
fix-remove-service-bug
Albertlarsen Sep 5, 2023
4a761a3
fix lint errors
Albertlarsen Sep 5, 2023
ce0e5c4
Last code improvement
Albertlarsen Sep 6, 2023
1a1062e
Last code improvements
Albertlarsen Sep 6, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
"status": "NotDelegable",
"details": [
{
"code": "Unknown",
"description": "Unknown",
"code": "MissingSrrRightAccess",
"description": "MissingSrrRightAccess",
"parameters": [
{
"name": "RoleAccess",
Expand All @@ -57,8 +57,8 @@
"status": "NotDelegable",
"details": [
{
"code": "Unknown",
"description": "Unknown",
"code": "MissingSrrRightAccess",
"description": "MissingSrrRightAccess",
"parameters": [
{
"name": "RoleAccess",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"vite-plugin-svgr": "^3.0.0"
},
"devDependencies": {
"@digdir/design-system-tokens": "^0.4.1",
"@digdir/design-system-tokens": "^0.4.2",
"@testing-library/cypress": "^9.0.0",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export const SummaryPage = ({
nextPath={
'/' + ApiDelegationPath.OfferedApiDelegations + '/' + ApiDelegationPath.Receipt
}
nextText={t('api_delegation.confirm_delegation')}
nextText={t('common.confirm')}
allinox marked this conversation as resolved.
Show resolved Hide resolved
nextDisabled={confirmationButtonDisabled}
nextLoading={confirmationButtonLoading}
nextButtonColor='success'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.popoverButtonContainer {
margin-top: 20px;
display: flex;
justify-content: center;
}

.serviceResources {
margin-top: var(--fds-spacing-2);
display: flex;
flex-direction: column;
gap: var(--fds-spacing-2);
}

.serviceResourceContent {
display: flex;
flex-direction: column;
gap: 6px;
}

.chipContainer {
display: flex;
gap: 5px;
margin-top: 15px;
}

.navigationContainer {
margin-top: 40px;
}

.secondaryText {
margin-top: 20px;
margin-bottom: 40px;
}

.alertContainer {
max-width: 500px;
margin-top: 30px;
}
allinox marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,17 +1,239 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { PersonIcon } from '@navikt/aksel-icons';
import { Alert, Button, Chip, Ingress, Paragraph, Popover } from '@digdir/design-system-react';
import { useNavigate } from 'react-router-dom';
import { useMemo, useState } from 'react';

import { Page, PageContainer, PageContent, PageHeader } from '@/components';
import { DualElementsContainer, Page, PageContainer, PageContent, PageHeader } from '@/components';
import { useAppDispatch, useAppSelector } from '@/rtk/app/hooks';
import { SingleRightPath } from '@/routes/paths';
import { useMediaQuery } from '@/resources/hooks';
import {
type ChosenService,
removeServiceResource,
} from '@/rtk/features/singleRights/singleRightsSlice';
import { getSingleRightsErrorCodeTextKey } from '@/resources/utils/errorCodeUtils';

import { ResourceActionBar } from '../ChooseServicePage/ResourceActionBar/ResourceActionBar';

import classes from './ChooseRightsPage.module.css';

interface DelegationResourceDTO {
title: string | undefined;
serviceIdentifier: string | undefined;
rightKey: string;
checked: boolean;
}

export const ChooseRightsPage = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const dispatch = useAppDispatch();
const [popoverOpen, setPopoverOpen] = useState(false);
const [checkedStates, setCheckedStates] = useState<Record<string, boolean>>({});
const isSm = useMediaQuery('(max-width: 768px)');
const rightsToBeDelegated: DelegationResourceDTO[] = [];
allinox marked this conversation as resolved.
Show resolved Hide resolved

const chosenServices = useAppSelector((state) => state.singleRightsSlice.chosenServices);

const delegableChosenServices = chosenServices.filter((s) => s.status !== 'NotDelegable');

const onRemove = (identifier: string | undefined) => {
void dispatch(removeServiceResource(identifier));
};

const onConfirm = () => {
setPopoverOpen(!popoverOpen);
const filteredList = rightsToBeDelegated.filter((right) => right.checked);
const listWithoutChecked = filteredList.map(({ checked, ...rest }) => rest);

Check warning on line 50 in src/features/singleRight/delegate/ChooseRightsPage/ChooseRightsPage.tsx

View workflow job for this annotation

GitHub Actions / Build

'listWithoutChecked' is assigned a value but never used
allinox marked this conversation as resolved.
Show resolved Hide resolved
};

const handleToggleChecked = (serviceIdentifier: string, rightKey: string) => {
setCheckedStates((prevCheckedStates) => ({
allinox marked this conversation as resolved.
Show resolved Hide resolved
...prevCheckedStates,
[`${serviceIdentifier}-${rightKey}`]: !prevCheckedStates[`${serviceIdentifier}-${rightKey}`],
}));
};

const sortedServiceResources = useMemo(() => {
allinox marked this conversation as resolved.
Show resolved Hide resolved
return [...delegableChosenServices]?.sort((a, b) => {
const isPartiallyDelegableA = a.status === 'PartiallyDelegable';
const isPartiallyDelegableB = b.status === 'PartiallyDelegable';

if (isPartiallyDelegableA && !isPartiallyDelegableB) {
return -1;
}
if (!isPartiallyDelegableA && isPartiallyDelegableB) {
return 1;
}

return a.service?.title.localeCompare(b.service.title);
});
}, [delegableChosenServices]);

const serviceResourcesActionBars = useMemo(() => {
allinox marked this conversation as resolved.
Show resolved Hide resolved
allinox marked this conversation as resolved.
Show resolved Hide resolved
return sortedServiceResources?.map((chosenService: ChosenService) => {
let hasPartiallyDelegableAppeared = false;
const isPartiallyDelegable =
chosenService.status === 'PartiallyDelegable' && !hasPartiallyDelegableAppeared;
allinox marked this conversation as resolved.
Show resolved Hide resolved

if (isPartiallyDelegable) {
hasPartiallyDelegableAppeared = true;
allinox marked this conversation as resolved.
Show resolved Hide resolved
}

return (
<ResourceActionBar
key={chosenService.service?.identifier}
title={chosenService.service?.title}
subtitle={chosenService.service?.resourceOwnerName}
status={chosenService.status ?? 'Unchecked'}
allinox marked this conversation as resolved.
Show resolved Hide resolved
onRemoveClick={() => {
onRemove(chosenService.service?.identifier);
}}
compact={isSm}
canBePartiallyDelegable={true}
initialOpen={isPartiallyDelegable}
>
<div className={classes.serviceResourceContent}>
<Paragraph spacing>{chosenService.service?.description}</Paragraph>
<Paragraph spacing>{chosenService.service?.rightDescription}</Paragraph>
<Paragraph spacing>{t('single_rights.action_bar_adjust_rights_text')}</Paragraph>
allinox marked this conversation as resolved.
Show resolved Hide resolved
<Paragraph>{t('single_rights.choose_rights_chip_text')}</Paragraph>
allinox marked this conversation as resolved.
Show resolved Hide resolved
<div className={classes.chipContainer}>
{chosenService.accessCheckResponses
?.filter((response) => response.status !== 'NotDelegable')
.map((response, index: number) => {
const isChecked =
!checkedStates[`${chosenService.service?.identifier}-${response.rightKey}`];
const dto = {
title: chosenService.service?.title,
serviceIdentifier: chosenService.service?.identifier,
rightKey: response.rightKey,
checked: isChecked,
};
rightsToBeDelegated.push(dto);
allinox marked this conversation as resolved.
Show resolved Hide resolved

return (
<div key={index}>
<Chip.Toggle
checkmark
selected={isChecked}
onClick={() => {
handleToggleChecked(chosenService.service?.identifier, response.rightKey);
}}
>
{t(`common.${response.action}`)}
</Chip.Toggle>
</div>
);
})}
</div>
{chosenService.status === 'PartiallyDelegable' && (
<div className={classes.alertContainer}>
<Alert severity='warning'>
<Paragraph
Albertlarsen marked this conversation as resolved.
Show resolved Hide resolved
size={'large'}
spacing
>
{t('single_rights.alert_partially_delegable_header')}
</Paragraph>
<Paragraph spacing>
{t(`${getSingleRightsErrorCodeTextKey(chosenService.errorCode)}`)}
</Paragraph>
<Paragraph>{t('single_rights.you_cant_delegate_these_rights')}</Paragraph>
<div className={classes.chipContainer}>
{chosenService.accessCheckResponses
?.filter((response) => response.status === 'NotDelegable')
allinox marked this conversation as resolved.
Show resolved Hide resolved
.map((response, index: number) => {
return (
<div key={index}>
<Chip.Toggle>{t(`common.${response.action}`)}</Chip.Toggle>
</div>
);
})}
</div>
</Alert>
</div>
)}
</div>
</ResourceActionBar>
);
});
}, [sortedServiceResources]);

const navigationButtons = () => {
return (
<DualElementsContainer
leftElement={
<Button
variant='outline'
color='primary'
fullWidth={true}
onClick={() => {
navigate(
'/' + SingleRightPath.DelegateSingleRights + '/' + SingleRightPath.ChooseService,
);
}}
>
{t('common.previous')}
</Button>
}
rightElement={
<Popover
placement={'top'}
trigger={
<Button
variant='filled'
color='primary'
fullWidth={true}
onClick={() => {
setPopoverOpen(!popoverOpen);
}}
disabled={delegableChosenServices.length < 1}
Albertlarsen marked this conversation as resolved.
Show resolved Hide resolved
>
{t('common.complete')}
</Button>
}
open={popoverOpen}
allinox marked this conversation as resolved.
Show resolved Hide resolved
onOpenChange={() => {
setPopoverOpen(!popoverOpen);
}}
variant={'info'}
>
<Paragraph>
{t('single_rights.confirm_delegation_text', { name: 'ANNEMA FIGMA' })}
</Paragraph>
<div className={classes.popoverButtonContainer}>
<Button
onClick={() => {
onConfirm();
}}
>
{t('common.confirm_delegation')}
</Button>
</div>
</Popover>
}
/>
);
};

return (
<PageContainer>
<Page color='light'>
<PageHeader icon={<PersonIcon />}>{t('single_rights.delegate_single_rights')}</PageHeader>
<PageContent></PageContent>
<PageContent>
<Ingress>
{t('single_rights.choose_rights_page_top_text', { name: 'ANNEMA FIGMA' })}
</Ingress>
<div className={classes.secondaryText}>
<Paragraph>{t('single_rights.choose_rights_page_secondary_text')}</Paragraph>
</div>
<div className={classes.serviceResources}>{serviceResourcesActionBars}</div>
<div className={classes.navigationContainer}>{navigationButtons()}</div>
</PageContent>
</Page>
</PageContainer>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
flex-wrap: wrap;
}

.serviceResouces {
.serviceResources {
margin-top: var(--fds-spacing-2);
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -85,3 +85,11 @@
width: 100%;
}
}

.pageContent {
min-height: 1200px;
allinox marked this conversation as resolved.
Show resolved Hide resolved
}

.navigationButtons {
margin-top: 4rem;
}
Loading
Loading