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(ojoi): PDF generation #16698

Merged
merged 12 commits into from
Nov 4, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { GetUserInvolvedPartiesResponse } from '../models/getUserInvolvedParties
import { GetUserInvolvedPartiesInput } from '../models/getUserInvolvedParties.input'
import { OJOIAIdInput } from '../models/id.input'
import { OJOIAApplicationCaseResponse } from '../models/applicationCase.response'
import { GetPdfResponse } from '../models/getPdf.response'

@Scopes(ApiScope.internal)
@UseGuards(IdsUserGuard, ScopesGuard)
Expand Down Expand Up @@ -71,6 +72,13 @@ export class OfficialJournalOfIcelandApplicationResolver {
return this.ojoiApplicationService.getPdfUrl(id, user)
}

@Query(() => GetPdfResponse, {
name: 'OJOIAGetPdf',
})
getPdf(@Args('input') input: OJOIAIdInput, @CurrentUser() user: User) {
return this.ojoiApplicationService.getPdf(input, user)
}

@Mutation(() => GetPresignedUrlResponse, {
name: 'officialJournalOfIcelandApplicationGetPresignedUrl',
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
GetCommentsResponse,
} from '../models/getComments.response'
import { OJOIAApplicationCaseResponse } from '../models/applicationCase.response'
import { GetPdfResponse } from '../models/getPdf.response'
import { OJOIAIdInput } from '../models/id.input'

const LOG_CATEGORY = 'official-journal-of-iceland-application'

Expand Down Expand Up @@ -238,4 +240,17 @@ export class OfficialJournalOfIcelandApplicationService {

return mapped
}

async getPdf(input: OJOIAIdInput, user: User): Promise<GetPdfResponse> {
const data = await this.ojoiApplicationService.getPdf(
{
id: input.id,
},
user,
)

return {
pdf: data.content,
}
}
jonbjarnio marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Field, ObjectType } from '@nestjs/graphql'

@ObjectType('OJOIAGetPdfResponse')
export class GetPdfResponse {
@Field()
pdf!: string
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
Bullet,
BulletList,
Button,
SkeletonLoader,
Stack,
Text,
} from '@island.is/island-ui/core'
Expand All @@ -14,6 +13,7 @@ import { OJOIFieldBaseProps } from '../lib/types'
import { useLocale } from '@island.is/localization'
import { HTMLText } from '@island.is/regulations-tools/types'
import {
base64ToBlob,
getAdvertMarkup,
getSignaturesMarkup,
parseZodIssue,
Expand All @@ -23,11 +23,11 @@ import { useApplication } from '../hooks/useUpdateApplication'
import { advert, error, preview, signatures } from '../lib/messages'
import { useType } from '../hooks/useType'
import {
advertValidationSchema,
previewValidationSchema,
signatureValidationSchema,
} from '../lib/dataSchema'
import { ZodCustomIssue } from 'zod'
import { usePdf } from '../hooks/usePdf'

export const Preview = ({ application, goToScreen }: OJOIFieldBaseProps) => {
const { application: currentApplication } = useApplication({
Expand All @@ -36,15 +36,44 @@ export const Preview = ({ application, goToScreen }: OJOIFieldBaseProps) => {

const { formatMessage: f } = useLocale()

const { type, loading } = useType({
const { type } = useType({
typeId: currentApplication.answers.advert?.typeId,
})

if (loading) {
return (
<SkeletonLoader height={40} space={2} repeat={5} borderRadius="large" />
)
}
const {
fetchPdf,
error: pdfError,
loading: pdfLoading,
} = usePdf({
applicationId: application.id,
onComplete: (data) => {
const blob = base64ToBlob(data.OJOIAGetPdf.pdf)
const url = URL.createObjectURL(blob)

let downloadName
const type = currentApplication.answers.advert?.typeName
if (type) {
downloadName = type.replace('.', '')
}

const title = currentApplication.answers.advert?.title
if (title) {
downloadName += ` ${title}`
}

if (!downloadName) {
downloadName = `Innsending ${application.id}`
}

downloadName += '.pdf'

const anchor = document.createElement('a')
anchor.href = url
anchor.download = downloadName
anchor.click()
anchor.remove()
},
})
jonbjarnio marked this conversation as resolved.
Show resolved Hide resolved

const advertValidationCheck = previewValidationSchema.safeParse(
currentApplication.answers,
Expand Down Expand Up @@ -77,12 +106,32 @@ export const Preview = ({ application, goToScreen }: OJOIFieldBaseProps) => {

return (
<Stack space={4}>
<Box>
<Button
loading={pdfLoading}
icon="download"
iconType="outline"
variant="utility"
onClick={() => fetchPdf()}
>
{f(preview.buttons.fetchPdf)}
</Button>
</Box>
<Box
hidden={
advertValidationCheck.success && signatureValidationCheck.success
advertValidationCheck.success &&
signatureValidationCheck.success &&
!error
}
>
<Stack space={2}>
{pdfError && (
<AlertMessage
type="error"
title={f(preview.errors.pdfError)}
message={f(preview.errors.pdfErrorMessage)}
/>
)}
jonbjarnio marked this conversation as resolved.
Show resolved Hide resolved
{!advertValidationCheck.success && (
<AlertMessage
type="warning"
Expand All @@ -91,13 +140,9 @@ export const Preview = ({ application, goToScreen }: OJOIFieldBaseProps) => {
<Stack space={2}>
<Text>{f(preview.errors.noContentMessage)}</Text>
<BulletList color="black">
{advertValidationCheck.error.issues.map((issue) => {
{advertValidationCheck.error.issues.map((issue, i) => {
const parsedIssue = parseZodIssue(issue as ZodCustomIssue)
return (
<Bullet key={issue.path.join('.')}>
{f(parsedIssue.message)}
</Bullet>
)
return <Bullet key={i}>{f(parsedIssue.message)}</Bullet>
jonbjarnio marked this conversation as resolved.
Show resolved Hide resolved
})}
</BulletList>
<Button
Expand Down Expand Up @@ -136,13 +181,9 @@ export const Preview = ({ application, goToScreen }: OJOIFieldBaseProps) => {
})}
</Text>
<BulletList color="black">
{signatureValidationCheck.error.issues.map((issue) => {
{signatureValidationCheck.error.issues.map((issue, i) => {
const parsedIssue = parseZodIssue(issue as ZodCustomIssue)
return (
<Bullet key={issue.path.join('.')}>
{f(parsedIssue.message)}
</Bullet>
)
return <Bullet key={i}>{f(parsedIssue.message)}</Bullet>
})}
</BulletList>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,11 @@ export const GET_APPLICATION_CASE_QUERY = gql`
}
}
`

export const GET_PDF_QUERY = gql`
query GetPdf($input: OJOIAIdInput!) {
OJOIAGetPdf(input: $input) {
pdf
}
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useLazyQuery } from '@apollo/client'
import { OjoiaGetPdfResponse } from '@island.is/api/schema'
import { GET_PDF_QUERY } from '../graphql/queries'

type Props = {
applicationId: string
onComplete?: (data: { OJOIAGetPdf: OjoiaGetPdfResponse }) => void
}
jonbjarnio marked this conversation as resolved.
Show resolved Hide resolved

export const usePdf = ({ applicationId, onComplete }: Props) => {
const [fetchPdf, { data, loading, error }] = useLazyQuery<{
OJOIAGetPdf: OjoiaGetPdfResponse
}>(GET_PDF_QUERY, {
variables: {
input: {
id: applicationId,
},
},
onCompleted: onComplete,
})

return {
fetchPdf,
pdf: data?.OJOIAGetPdf?.pdf,
loading,
error,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ export const preview = {
defaultMessage: 'Að lágmarki þarf að fylla út',
description: 'Error message when content is missing',
},
pdfError: {
id: 'ojoi.application:preview.errors.pdfError',
defaultMessage: 'Villa kom upp við að sækja skjal',
description: 'Error message when pdf download fails',
},
pdfErrorMessage: {
id: 'ojoi.application:preview.errors.pdfErrorMessage',
defaultMessage: 'Ekki tókst að sækja skjal, reyndu aftur',
description: 'Error message when pdf download fails',
},
jonbjarnio marked this conversation as resolved.
Show resolved Hide resolved
}),
buttons: defineMessages({
fetchPdf: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,13 @@ export const getFastTrack = (date?: Date) => {
now,
}
}

export const base64ToBlob = (base64: string) => {
const byteCharacters = Buffer.from(base64, 'base64')
const byteNumbers = new Array(byteCharacters.length)
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters[i]
}
const byteArray = new Uint8Array(byteNumbers)
return new Blob([byteArray], { type: 'application/pdf' })
}
jonbjarnio marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@
"description": "",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/StreamableFile" }
"schema": { "$ref": "#/components/schemas/GetPdfRespone" }
jonbjarnio marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Expand All @@ -368,7 +368,7 @@
"description": "",
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/StreamableFile" }
"schema": { "$ref": "#/components/schemas/GetPdfRespone" }
}
}
}
Expand Down Expand Up @@ -473,6 +473,11 @@
"example": "a12c3d4e-5f67-8h90-1i23-j45k6l7m8n9o0",
"description": "Id of the selected type"
},
"typeName": {
"type": "string",
"example": "a12c3d4e-5f67-8h90-1i23-j45k6l7m8n9o0",
"description": "Title of the selected type"
},
"title": {
"type": "string",
"example": "a12c3d4e-5f67-8h90-1i23-j45k6l7m8n9o0",
Expand Down Expand Up @@ -506,6 +511,7 @@
"involvedPartyId",
"departmentId",
"typeId",
"typeName",
"title",
"html",
"requestedDate",
Expand Down Expand Up @@ -1083,6 +1089,35 @@
},
"required": ["id", "title", "slug"]
},
"CaseStatus": {
"type": "object",
"properties": {
"id": {
"type": "string",
"example": "d290f1ee-6c54-4b01-90e6-d701748f0851"
},
"title": {
"type": "string",
"enum": [
"Innsent",
"Grunnvinnsla",
"Yfirlestur",
"Tilbúið",
"Útgefið",
"Tekið úr birtingu",
"Birtingu hafnað"
],
"example": "Innsent",
"description": "Status of the case"
},
"slug": {
"type": "string",
"example": "innsent",
"description": "Slug of the case staus"
}
},
"required": ["id", "title", "slug"]
},
"CommunicationStatus": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -1163,7 +1198,10 @@
"type": "array",
"items": { "$ref": "#/components/schemas/Category" }
},
"status": { "type": "object" },
"status": {
"description": "Current status of the case",
"allOf": [{ "$ref": "#/components/schemas/CaseStatus" }]
},
"communicationStatus": {
"$ref": "#/components/schemas/CommunicationStatus"
},
Expand All @@ -1187,7 +1225,13 @@
},
"required": ["applicationCase"]
},
"StreamableFile": { "type": "object", "properties": {} },
"GetPdfRespone": {
"type": "object",
"properties": {
"content": { "type": "string", "description": "Base64 encoded PDF" }
},
"required": ["content"]
},
"GetPdfUrlResponse": {
"type": "object",
"properties": { "url": { "type": "string" } },
Expand Down
Loading
Loading