Skip to content

Commit

Permalink
Merge pull request #1091 from digitalfabrik/1075-create-card-from-app…
Browse files Browse the repository at this point in the history
…lication

1075: Create card from application
  • Loading branch information
sarahsporck authored Sep 22, 2023
2 parents 0d819c4 + 8950d9a commit 7bde1ae
Show file tree
Hide file tree
Showing 27 changed files with 782 additions and 515 deletions.
1 change: 1 addition & 0 deletions administration/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/prefer-ts-expect-error": "error",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/ban-types": ["error", { "extendDefaults": true, "types": { "{}": false } }]
}
}
24 changes: 12 additions & 12 deletions administration/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions administration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"dependencies": {
"@apollo/client": "^3.7.16",
"@blueprintjs/core": "^5.0.0",
"@blueprintjs/core": "^5.3.0",
"@blueprintjs/select": "^5.0.0",
"@blueprintjs/table": "^5.0.0",
"@bufbuild/protobuf": "^1.2.1",
Expand Down Expand Up @@ -57,8 +57,8 @@
"bfj": "^7.0.2",
"browserslist": "^4.21.9",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"css-minimizer-webpack-plugin": "^5.0.1",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"dotenv": "^16.3.1",
"dotenv-expand": "^10.0.0",
"eslint": "^8.48.0",
Expand Down
6 changes: 3 additions & 3 deletions administration/src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const Router = () => {
{ path: '/forgot-password', element: <ForgotPasswordController /> },
{ path: '/reset-password/', element: <ResetPasswordController /> },
{ path: '/data-privacy-policy', element: <DataPrivacyPolicy /> },
...(projectConfig.applicationFeatureEnabled
...(projectConfig.applicationFeature
? [
{ path: '/beantragen', element: <ApplyController /> },
{
Expand All @@ -68,7 +68,7 @@ const Router = () => {
</WhoAmIProvider>
),
children: [
...(projectConfig.applicationFeatureEnabled
...(projectConfig.applicationFeature
? [
{ path: 'applications', element: <ApplicationsController /> },
{ path: 'region/data-privacy-policy', element: <DataPrivacyController /> },
Expand All @@ -86,7 +86,7 @@ const Router = () => {
},
]
return createBrowserRouter(routes.filter((element): element is RouteObject => element !== null))
}, [authData, projectConfig.applicationFeatureEnabled, signIn, signOut])
}, [authData, projectConfig.applicationFeature, signIn, signOut])

return <RouterProvider router={router} />
}
Expand Down
4 changes: 2 additions & 2 deletions administration/src/bp-modules/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const Navigation = (props: Props) => {
<Navbar.Divider />
{role === Role.RegionAdmin || role === Role.RegionManager ? (
<>
{config.applicationFeatureEnabled ? (
{config.applicationFeature ? (
<NavLink to={'/applications'}>
<Button minimal icon='form' text='Eingehende Anträge' />
</NavLink>
Expand All @@ -53,7 +53,7 @@ const Navigation = (props: Props) => {
<Button minimal icon='people' text='Benutzer verwalten' />
</NavLink>
) : null}
{role === Role.RegionAdmin && config.applicationFeatureEnabled ? (
{role === Role.RegionAdmin && config.applicationFeature ? (
<NavLink to={'/region'}>
<Button minimal icon='path-search' text='Region verwalten' />
</NavLink>
Expand Down
209 changes: 209 additions & 0 deletions administration/src/bp-modules/applications/ApplicationCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import {
Alert,
AnchorButton,
Button,
Callout,
Colors,
H4,
Icon,
Section,
SectionCard,
Tooltip,
} from '@blueprintjs/core'
import { memo, useContext, useMemo, useState } from 'react'
import styled, { css } from 'styled-components'

import getMessageFromApolloError from '../../errors/getMessageFromApolloError'
import { useDeleteApplicationMutation } from '../../generated/graphql'
import { ProjectConfigContext } from '../../project-configs/ProjectConfigContext'
import formatDateWithTimezone from '../../util/formatDate'
import getApiBaseUrl from '../../util/getApiBaseUrl'
import { useAppToaster } from '../AppToaster'
import { Application } from './ApplicationsOverview'
import JsonFieldView, { JsonField } from './JsonFieldView'
import VerificationsView, { VerificationsQuickIndicator } from './VerificationsView'

export const printAwareCss = css`
@media print {
display: none;
}
`

const ApplicationViewCard = styled(Section)<{ $hideInPrintMode?: boolean }>`
width: 1000px;
max-width: 90%;
overflow: hidden;
margin: 10px;
position: relative;
@media print {
width: 100%;
height: auto;
box-shadow: none;
}
${props => props.$hideInPrintMode && printAwareCss};
`

const ButtonContainer = styled.div`
display: flex;
flex-grow: 1;
margin-top: 10px;
`

export const CollapseIcon = styled(Icon)`
display: block;
margin-left: auto;
align-self: center;
padding: 2px;
${printAwareCss};
:hover {
cursor: pointer;
color: ${Colors.GRAY1};
}
`

const WithdrawAlert = styled(Callout)`
margin-bottom: 16px;
`

const PrintAwareButton = styled(Button)`
margin-right: 10px;
${printAwareCss};
`

const PrintAwareAnchorButton = styled(AnchorButton)`
margin-right: 10px;
${printAwareCss};
`

const Title = styled(H4)`
margin: 0;
display: inline-block;
`

const CardContentHint = styled(Title)`
color: ${Colors.GRAY1};
`

type ApplicationCardProps = {
application: Application
gotDeleted: () => void
printApplicationById: (applicationId: number) => void
isSelectedForPrint: boolean
}

const ApplicationCard = ({
application,
gotDeleted,
printApplicationById,
isSelectedForPrint,
}: ApplicationCardProps) => {
const [isExpanded, setIsExpanded] = useState(false)
const { createdDate: createdDateString, jsonValue, id, withdrawalDate } = application
const jsonField: JsonField<'Array'> = JSON.parse(jsonValue)
const config = useContext(ProjectConfigContext)
const baseUrl = `${getApiBaseUrl()}/application/${config.projectId}/${id}`
const appToaster = useAppToaster()
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
const [deleteApplication, { loading }] = useDeleteApplicationMutation({
onError: error => {
const { title } = getMessageFromApolloError(error)
appToaster?.show({ intent: 'danger', message: title })
},
onCompleted: ({ deleted }: { deleted: boolean }) => {
if (deleted) {
gotDeleted()
} else {
console.error('Delete operation returned false.')
appToaster?.show({ intent: 'danger', message: 'Etwas ist schief gelaufen.' })
}
},
})

const createCardQuery = useMemo(
() => config.applicationFeature?.applicationJsonToCardQuery(jsonField),
[config.applicationFeature, jsonField]
)

const personalData = useMemo(
() => config.applicationFeature?.applicationJsonToPersonalData(jsonField),
[config.applicationFeature, jsonField]
)

return (
<ApplicationViewCard
title={
<div>
<Title>Antrag vom {formatDateWithTimezone(createdDateString, config.timezone)}&emsp;</Title>{' '}
{personalData && personalData.forenames && personalData.surname && (
<CardContentHint>
Name: {personalData.surname}, {personalData.forenames}
</CardContentHint>
)}
</div>
}
rightElement={<VerificationsQuickIndicator verifications={application.verifications} />}
elevation={1}
icon={withdrawalDate ? <Icon icon='warning-sign' intent='warning' /> : undefined}
collapseProps={{ isOpen: isExpanded, onToggle: () => setIsExpanded(!isExpanded), keepChildrenMounted: true }}
collapsible
$hideInPrintMode={!isSelectedForPrint}>
<SectionCard>
{withdrawalDate && (
<WithdrawAlert intent='warning'>
Der Antrag wurde vom Antragssteller am {formatDateWithTimezone(withdrawalDate, config.timezone)}{' '}
zurückgezogen. <br />
Bitte löschen Sie den Antrag zeitnah.
</WithdrawAlert>
)}
<JsonFieldView
jsonField={jsonField}
baseUrl={baseUrl}
key={0}
hierarchyIndex={0}
attachmentAccessible
expandedRoot={false}
/>
</SectionCard>
<SectionCard>
<VerificationsView verifications={application.verifications} />
</SectionCard>
<SectionCard>
<ButtonContainer>
<Tooltip
disabled={!!createCardQuery}
content={
'Es existiert kein passendes Mapping, um aus diesem Antrag das Kartenformular vollständig auszufüllen.'
}>
<PrintAwareAnchorButton
disabled={!createCardQuery}
href={createCardQuery ? `./cards/add${createCardQuery}` : undefined}
icon='id-number'
intent='primary'>
Karte erstellen
</PrintAwareAnchorButton>
</Tooltip>
<PrintAwareButton onClick={() => setDeleteDialogOpen(true)} intent='danger' icon='trash'>
Antrag löschen
</PrintAwareButton>
<PrintAwareButton onClick={() => printApplicationById(id)} intent='none' icon='print'>
PDF exportieren
</PrintAwareButton>
<CollapseIcon icon={'chevron-up'} onClick={() => setIsExpanded(!isExpanded)} style={{ marginLeft: 'auto' }} />
</ButtonContainer>
<Alert
cancelButtonText='Abbrechen'
confirmButtonText='Antrag löschen'
icon='trash'
intent='danger'
isOpen={deleteDialogOpen}
loading={loading}
onCancel={() => setDeleteDialogOpen(false)}
onConfirm={() => deleteApplication({ variables: { applicationId: application.id } })}>
<p>Möchten Sie den Antrag unwiderruflich löschen?</p>
</Alert>
</SectionCard>
</ApplicationViewCard>
)
}

export default memo(ApplicationCard)
Loading

0 comments on commit 7bde1ae

Please sign in to comment.