Skip to content

Commit

Permalink
feat(auth-admin): View delegation and refactor access card (#16243)
Browse files Browse the repository at this point in the history
* refactor AccessCard component and make change to modal to view delegation in access control

* remove unused createdBy

* chore: nx format:write update dirty files

* refactor

* fix typo

* chore: nx format:write update dirty files

* fix type

* small refactor

* fix delete function

* chore: nx format:write update dirty files

* Adds delete modal

* chore: nx format:write update dirty files

* Update DelegationDeleteModal.tsx

* adds loading to create modal

* chore: nx format:write update dirty files

* small fixes and use view delegation modal in admin portal

* Update AccessCard.tsx

* chore: nx format:write update dirty files

* fix query and types

* chore: nx format:write update dirty files

---------

Co-authored-by: andes-it <[email protected]>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 9, 2024
1 parent 386d587 commit acd65c3
Show file tree
Hide file tree
Showing 23 changed files with 608 additions and 242 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,17 @@ export class DelegationAdminResolver {
return identityLoader.load(customDelegation.toNationalId)
}

@ResolveField('createdBy', () => Identity, { nullable: true })
resolveCreatedByIdentity(
@Loader(IdentityLoader) identityLoader: IdentityDataLoader,
@Parent() customDelegation: DelegationDTO,
) {
if (!customDelegation.createdByNationalId) {
return null
}
return identityLoader.load(customDelegation.createdByNationalId)
}

@ResolveField('validTo', () => Date, { nullable: true })
resolveValidTo(@Parent() delegation: DelegationDTO): Date | undefined {
if (!delegation.validTo) {
Expand Down
3 changes: 3 additions & 0 deletions libs/api/domains/auth/src/lib/models/delegation.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export abstract class Delegation {
@Field(() => Identity)
to!: Identity

@Field(() => Identity, { nullable: true })
createdBy?: Identity

@Field(() => AuthDelegationType)
type!: AuthDelegationType

Expand Down
11 changes: 11 additions & 0 deletions libs/api/domains/auth/src/lib/resolvers/delegation.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,17 @@ export class DelegationResolver {
)
}

@ResolveField('createdBy', () => Identity, { nullable: true })
async resolveCreatedBy(
@Parent() delegation: DelegationDTO,
): Promise<Identity | null> {
if (!delegation.createdByNationalId) {
return null
}

return this.identityService.getIdentity(delegation.createdByNationalId)
}

@ResolveField('validTo', () => Date, { nullable: true })
resolveValidTo(@Parent() delegation: DelegationDTO): Date | undefined {
if (!delegation.validTo) {
Expand Down
5 changes: 5 additions & 0 deletions libs/api/mocks/src/domains/auth/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export const customDelegation = factory<AuthCustomDelegation>({
name: faker.name.findName(),
type: 'Person',
}),
createdBy: () => ({
nationalId: createNationalId('person'),
name: faker.name.findName(),
type: 'Person',
}),
type: 'Custom',
provider: 'delegationdb',
scopes: () => delegationScope.list(5),
Expand Down
5 changes: 5 additions & 0 deletions libs/auth-api-lib/src/lib/delegations/dto/delegation.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ export class DelegationDTO {
@IsString()
referenceId?: string | null

@IsOptional()
@IsString()
@ApiPropertyOptional({ nullable: true, type: String })
createdByNationalId?: string | null

@IsOptional()
@ApiPropertyOptional({ type: [DelegationScopeDTO] })
@IsArray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export class Delegation extends Model<
fromNationalId: this.fromNationalId,
toNationalId: this.toNationalId,
toName: this.toName,
createdByNationalId: this.createdByNationalId,
validTo: this.validTo,
scopes: this.delegationScopes
? this.delegationScopes.map((scope) => scope.toDTO())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ type CreateDelegationConfirmModalProps = Pick<
referenceId: string
validTo?: string | undefined
} | null
loading: boolean
onConfirm(): void
}

export const CreateDelegationConfirmModal = ({
fromIdentity,
toIdentity,
data,
loading,
onClose,
onConfirm,
...rest
Expand All @@ -43,10 +45,6 @@ export const CreateDelegationConfirmModal = ({
isDisabled: !rest.isVisible,
})

const typeLabels: Record<string, string> = {
general: formatMessage(m.typeGeneral), // Todo: use enum yet to be created
}

return (
<Modal
id="access-confirm-modal"
Expand Down Expand Up @@ -86,7 +84,7 @@ export const CreateDelegationConfirmModal = ({
{data?.type && (
<IdentityCard
label={formatMessage(m.type)}
title={typeLabels[data.type]}
title={formatMessage(m.generalMandateLabel)}
imgSrc="./assets/images/skjaldarmerki.svg"
/>
)}
Expand Down Expand Up @@ -120,6 +118,7 @@ export const CreateDelegationConfirmModal = ({
<Box position="sticky" bottom={0}>
<DelegationsFormFooter
showShadow={showShadow}
loading={loading}
onCancel={onClose}
onConfirm={onConfirm}
confirmLabel={formatMessage(coreMessages.codeConfirmation)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Box, useBreakpoint } from '@island.is/island-ui/core'
import { useLocale } from '@island.is/localization'
import { formatNationalId, m as coreMessages } from '@island.is/portals/core'
import { Modal, ModalProps } from '@island.is/react/components'
import { m } from '../lib/messages'
import {
DelegationsFormFooter,
IdentityCard,
useDynamicShadow,
} from '@island.is/portals/shared-modules/delegations'
import { AuthCustomDelegation } from '@island.is/api/schema'
import format from 'date-fns/format'
import isValid from 'date-fns/isValid'
import { AuthDelegationType } from '@island.is/shared/types'

type DelegationDeleteModalProps = Pick<
ModalProps,
'id' | 'onClose' | 'isVisible'
> & {
loading: boolean
delegation?: AuthCustomDelegation
onDelete(): void
}

export const DelegationDeleteModal = ({
id,
loading,
delegation,
onClose,
onDelete,
...rest
}: DelegationDeleteModalProps) => {
const { formatMessage } = useLocale()
const { md } = useBreakpoint()

const { showShadow, pxProps } = useDynamicShadow({
rootMargin: md ? '-128px' : '-104px',
isDisabled: !rest.isVisible,
})

return (
<Modal
id={id}
label={formatMessage(m.deleteDelegationModalTitle)}
title={formatMessage(m.deleteDelegationModalTitle)}
onClose={onClose}
noPaddingBottom
scrollType="inside"
closeButtonLabel={formatMessage(m.cancel)}
{...rest}
>
<Box marginY={4} display="flex" flexDirection="column" rowGap={3}>
<Box
width="full"
display="flex"
flexDirection={['column', 'column', 'column', 'row']}
rowGap={[3, 3, 3, 0]}
columnGap={[0, 0, 0, 3]}
>
{delegation?.from?.name && delegation?.from?.nationalId && (
<IdentityCard
label={formatMessage(m.fromNationalId)}
title={delegation?.from?.name}
description={formatNationalId(delegation?.from?.nationalId)}
color="blue"
/>
)}
{delegation?.to?.name && delegation?.to?.nationalId && (
<IdentityCard
label={formatMessage(m.toNationalId)}
title={delegation?.to.name}
description={formatNationalId(delegation?.to.nationalId)}
color="purple"
/>
)}
</Box>
{delegation?.type &&
delegation.type === AuthDelegationType.GeneralMandate && (
<IdentityCard
label={formatMessage(m.type)}
title={formatMessage(m.generalMandateLabel)}
imgSrc="./assets/images/skjaldarmerki.svg"
/>
)}

<Box
display="flex"
flexDirection={['column', 'row']}
justifyContent="spaceBetween"
columnGap={[0, 3]}
rowGap={[3, 0]}
>
<IdentityCard
label={formatMessage(m.validTo)}
title={
delegation?.validTo && isValid(delegation.validTo)
? format(new Date(delegation?.validTo), 'dd.MM.yyyy')
: formatMessage(m.noEndDate)
}
/>
{delegation?.referenceId && (
<IdentityCard
label={formatMessage(m.referenceId)}
title={delegation?.referenceId}
/>
)}
</Box>
</Box>

<div {...pxProps} />

<Box position="sticky" bottom={0}>
<DelegationsFormFooter
loading={loading}
showShadow={showShadow}
confirmButtonColorScheme="destructive"
onCancel={onClose}
onConfirm={onDelete}
confirmLabel={formatMessage(coreMessages.buttonDestroy)}
containerPaddingBottom={[3, 3, 6]}
/>
</Box>
</Modal>
)
}
Original file line number Diff line number Diff line change
@@ -1,45 +1,86 @@
import { AuthCustomDelegation } from '@island.is/api/schema'
import { Box, Stack } from '@island.is/island-ui/core'
import { AccessCard } from '@island.is/portals/shared-modules/delegations'
import {
AccessCard,
DelegationViewModal,
} from '@island.is/portals/shared-modules/delegations'
import { useDeleteCustomDelegationAdminMutation } from '../screens/DelegationAdminDetails/DelegationAdmin.generated'
import { useRevalidator } from 'react-router-dom'
import React, { useState } from 'react'
import { DelegationDeleteModal } from './DelegationDeleteModal'

interface DelegationProps {
direction: 'incoming' | 'outgoing'
delegationsList: AuthCustomDelegation[]
}

const DelegationList = ({ delegationsList, direction }: DelegationProps) => {
const [deleteCustomDelegationAdminMutation] =
const [deleteCustomDelegationAdminMutation, { loading }] =
useDeleteCustomDelegationAdminMutation()
const { revalidate } = useRevalidator()
const [delegationToDelete, setDelegationToDelete] =
useState<AuthCustomDelegation | null>(null)
const [delegationView, setDelegationView] =
useState<AuthCustomDelegation | null>(null)

const deleteHandler = async (id: string) => {
const { data } = await deleteCustomDelegationAdminMutation({
variables: {
id,
},
})
if (data) {
revalidate()
setDelegationToDelete(null)
}
}

return (
<Box marginTop={2}>
<Stack space={3}>
{delegationsList.map((delegation) => {
return (
<AccessCard
canModify={!!delegation.referenceId} // only allow deletion of paper delegations
direction={direction}
key={delegation.id}
delegation={delegation}
isAdminView={true}
onDelete={async () => {
const { data } = await deleteCustomDelegationAdminMutation({
variables: {
id: delegation.id as string,
},
})
if (data) {
revalidate()
<>
<Box marginTop={2}>
<Stack space={3}>
{delegationsList.map((delegation) => {
return (
<AccessCard
key={delegation.id}
delegation={delegation}
isAdminView
variant={direction}
onView={
delegation.referenceId
? (d) => setDelegationView(d)
: undefined
}
onDelete={
delegation.referenceId
? () => setDelegationToDelete(delegation)
: undefined
}
}}
/>
)
})}
</Stack>
</Box>
/>
)
})}
</Stack>
</Box>
<DelegationDeleteModal
id={`${direction}-delegation-delete-modal`}
delegation={delegationToDelete as AuthCustomDelegation}
loading={loading}
onClose={() => setDelegationToDelete(null)}
onDelete={() => {
if (delegationToDelete) {
deleteHandler(delegationToDelete.id as string)
}
}}
isVisible={!!delegationToDelete}
/>
<DelegationViewModal
onClose={() => setDelegationView(null)}
isVisible={!!delegationView}
delegation={delegationView || undefined}
direction={direction}
isAdminView
/>
</>
)
}

Expand Down
14 changes: 11 additions & 3 deletions libs/portals/admin/delegation-admin/src/lib/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,13 @@ export const m = defineMessages({
id: 'admin.delegationAdmin:create',
defaultMessage: 'Skrá umboð',
},
delete: {
id: 'admin.delegationAdmin:delete',
defaultMessage: 'Eyða umboði',
},
noEndDate: {
id: 'admin.delegationAdmin:noEndDate',
defaultMessage: 'Gildis tími óendanlegur',
defaultMessage: 'Gildistími óendanlegur',
},
validTo: {
id: 'admin.delegationAdmin:validTo',
Expand All @@ -73,8 +77,8 @@ export const m = defineMessages({
id: 'admin.delegationAdmin:type',
defaultMessage: 'Aðgangstegund',
},
typeGeneral: {
id: 'admin.delegationAdmin:typeGeneral',
generalMandateLabel: {
id: 'admin.delegationAdmin:generalMandateLabel',
defaultMessage: 'Allsherjarumboð',
},
referenceId: {
Expand Down Expand Up @@ -117,6 +121,10 @@ export const m = defineMessages({
id: 'admin.delegationAdmin:createDelegationConfirmModalTitle',
defaultMessage: 'Þú ert að skrá nýtt umboð',
},
deleteDelegationModalTitle: {
id: 'admin.delegationAdmin:deleteDelegationModalTitle',
defaultMessage: 'Eyða umboði',
},
createDelegationSuccessToast: {
id: 'admin.delegationAdmin:createDelegationSuccessToast',
defaultMessage: 'Umboð var skráð',
Expand Down
Loading

0 comments on commit acd65c3

Please sign in to comment.