PDF Generator

For physical applications of AdminJS a pdf generator can come in handy, either for generating shipping labels or work orders.

In order to provide PDF generating capabilities within your AdminJS instance you will have to take advantage of the custom components feature and the jsPDF library.

First, create a new instance of the Component Loader, and create a Components object that will hold the custom components.

index.ts
const componentLoader = new ComponentLoader()

const Components = {
    PDFGenerator: componentLoader.add('GeneratePDF', './pdfgenerator.component')
}

Then, we will have to append the PDF generator to a resource and add a handler for the record passed within context.

order.resource.ts
const orderResource = {
    resource: Order,
    options: {
        actions: {
            PDFGenerator: {
                actionType: 'record',
                icon: 'GeneratePdf',
                component: Components.PDFGenerator,
                handler: (request, response, context) => {
                    const { record, currentAdmin } = context
                    return {
                        record: record.toJSON(currentAdmin),
                        url: pdfgenerator(record.toJSON(currentAdmin))
                    }
                }
            }
        }
    }
}

With that ready, we can focus on the PDF generator function.

pdfgenerator.ts
import { RecordJSON } from 'adminjs'
import { jsPDF } from 'jspdf'

const pdfGenerator = (record: RecordJSON): string => {
  const { params } = record
  const doc = new jsPDF()
  
  doc.text(params.orderNum, 10, 10) // example database column called orderNum
  doc.text(params.shippingAddress, 150, 10) // example database column called shippingAddress
  
  const filename = `/${params.id}.pdf`
  doc.save(`./pdfs${filename}`)
  
  return filename
}

export default pdfGenerator

Now that our PDFs can be created from the record passed within context, we will need to create a place where the pdfs will be stored. Create a folder called 'pdfs', which we will make public through express.

.
├── index.ts
├── order.resource.ts
├── pdfHandler.ts
├── pdfgenerator.component.tsx
├── pdfgenerator.ts
└── pdfs
    └── ...

Make sure you set the static path before building the router!

index.ts
import path from 'path'
import * as url from 'url'
// other imports

const __dirname = url.fileURLToPath(new URL('.', import.meta.url))

// ...

app.use(express.static(path.join(__dirname, 'pdfs/')))

Last, but not least, we need a way to open the freshly generated PDF file. Since we've already defined the custom component we're going to stick to the same naming scheme.

pdfgenerator.component.tsx
import React, { useEffect } from 'react'
import { ApiClient, ActionProps } from 'adminjs'
import { Loader } from '@adminjs/design-system'

const GeneratePdf: React.FC<ActionProps> = (props) => {
  const { record, resource } = props
  const api = new ApiClient()

  useEffect(() => {
    api.recordAction({
      recordId: record.id,
      resourceId: resource.id,
      actionName: 'PDFGenerator'
    }).then((response) => {
      window.location.href = response.data.url
    }).catch((err) => {
      console.error(err)
    })
  }, [])

  return <Loader />
}

export default GeneratePdf

Last updated