Skip to content

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'id') using RTL Arabic #3195

@Momin-Mahmud

Description

@Momin-Mahmud

I am using a single component with dynamic data. When I select for example PDF (A) as my first PDF in dropdown and move to other PDFs it works fine. But when I select any other PDF and switch to PDF (A) it throws this error.
Also when I remove the arabic content through my getTranslation function it works fine. There is no data inconsistency issue from JSON.

IF I REMOVE ARABIC FONT IT WORKS FINE

Someone on this issue said #3050 (comment)
to use a different font it maybe a font issue but this is still happening

Code

Preview Component

import { useEffect, useState } from 'react'
import CertificatePDF from '../../../Certificates/CertificatePDF'
import { pdf } from '@react-pdf/renderer'
import { Document, Page, pdfjs } from 'react-pdf'
import classess from './BulkCertificate.module.scss'
import { useDebounce } from '../../../../hooks/useDebounce'
import Spinner from '../../../../components/shared/Spinner'

const CertificatePreview = ({
  certificate,
  setButtonDisable
}: {
  certificate: ICertification
  setButtonDisable: (value: boolean) => void
}) => {
  const [pdfUrl, setPdfUrl] = useState<string | null>(null)
  const [renderedCertificate, setRenderedCertificate] = useState<ICertification>(certificate)

  pdfjs.GlobalWorkerOptions.workerSrc = `https://esm.sh/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`

  const debouncedCertificateId = useDebounce(certificate?.id, 300)

  useEffect(() => {
    certificate?.id === debouncedCertificateId && setRenderedCertificate(certificate)
  }, [debouncedCertificateId, certificate])

  const generatePdf = async () => {
    const blob = await pdf(
      <CertificatePDF certificate={renderedCertificate} name='John Doe' alternativeName='فلان بن فلان' viewPDF />
    ).toBlob()

    const objectUrl = URL.createObjectURL(blob)
    setPdfUrl((prev) => {
      if (prev) URL.revokeObjectURL(prev)
      return objectUrl
    })
  }

  useEffect(() => {
    renderedCertificate?.id && generatePdf()
  }, [renderedCertificate?.id, renderedCertificate?.certificateTemplate?.id])

  useEffect(() => pdfUrl ?  setButtonDisable(false) : setButtonDisable(true), [pdfUrl])

  return (
    <div className={classess.certificate_view} key={renderedCertificate?.certificateTemplate?.id}>
      {pdfUrl ? (
        <Document
          file={pdfUrl}
          loading={<div className={classess.dummy_loader_certificate}><Spinner size='small' /></div>}
        >
          <Page
            pageNumber={1}
            loading={<div className={classess.dummy_loader_certificate} />}
            width={500}
            height={250}
            renderTextLayer={false}
            renderAnnotationLayer={false}
          />
        </Document>
      ) : (<div className={classess.dummy_loader_certificate}> <Spinner size='small'/></div>)}
    </div>
  )
}

export default CertificatePreview

PDF

import { Document, Page, Text, View, Image, StyleSheet } from '@react-pdf/renderer'
import { Font } from '@react-pdf/renderer'
import { formatToMonthDayYear } from '../../utils/helpers/string'

Font.register({
  family: 'DINNextLTArabic',
  src: '/fonts/DINNEXTLTARABIC-REGULAR.TTF',
  fontWeight: 400
})

Font.register({
  family: 'DINNextLTArabic',
  src: '/fonts/DINNEXTLTARABIC-BOLD.TTF',
  fontWeight: 700
})

type CertificatePDFProps = {
  certificate: ICertification
  user?: IUser
  name?:string
  alternativeName?:string
  event?: IEvent
  isEvent?: boolean
  qrCodeSrc?: string
  viewPDF?: boolean
}

const styles = StyleSheet.create({
  page: {
    fontFamily: 'DINNextLTArabic',
    flexDirection: 'column',
    alignItems: 'center',
    backgroundColor: '#ffffff',
    width: '100%',
    height: '100%',
    overflow: 'hidden'
  },

  logoContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 20,
    width: '100%'
  },
  logo: {
    width: 140,
    height: 60,
    marginBottom: 80,
    paddingTop: 20,
    paddingLeft: 35
  },
  bgImage: {
    width: 250
  },
  title: {
    fontFamily: 'DINNextLTArabic',
    fontSize: 15,
    fontWeight: 'bold',
    width: '40%',
    color: '#1f2937'
  },
  titleArabic: {
    fontFamily: 'DINNextLTArabic',
    fontSize: 13,
    fontWeight: 'bold',
    width: '40%',
    color: '#1f2937',
    textAlign: 'right'
  },
  subheading: {
    fontFamily: 'DINNextLTArabic',
    fontSize: 15,
    marginBottom: 20,
    textAlign: 'center'
  },
  certificateAchievementArabic: {
    fontFamily: 'DINNextLTArabic',
    fontSize: 24,
    color: '#143861',
    fontWeight: 'bold',
    marginBottom: 60,
    marginTop: -80,
    textAlign: 'center'
  },
  certificateAchievementEnglish: {
    fontFamily: 'DINNextLTArabic',
    fontSize: 24,
    color: '#143861',
    fontWeight: 'bold',
    marginBottom: 20,
    marginTop: -40,
    textAlign: 'center'
  },
  issuedTo: {
    fontFamily: 'DINNextLTArabic',
    fontSize: 24,
    marginBottom: 10,
    color: '#143861',
    textAlign: 'center',
    fontWeight: 'extrabold'
  },
  flexDescription: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '90%'
  },
  center: {
    flexDirection: 'row',
    justifyContent: 'center'
  },
  details: {
    fontFamily: 'DINNextLTArabic',
    fontSize: 15,
    marginBottom: 20,
    textAlign: 'center',
    color: '#1f2937',
    position: 'relative'
  },
  details1: {
    fontFamily: 'DINNextLTArabic',
    fontSize: 15,
    marginTop: 10,
    textAlign: 'center',
    color: '#1f2937'
  },
  dates: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    width: '100%',
    paddingHorizontal: 40,
    color: 'gray'
  },
  dateItem: {
    fontSize: 14
  },
  borderBottom: {
    backgroundColor: '#143861',
    width: '100%',
    height: 25,
    position: 'absolute',
    bottom: 0
  },
  footer: {
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    width: '90%'
  },
  stamp: {
    width: '118',
    height: '122'
  },
  signature: {
    color: '#B0B0B0',
    fontSize: 13
  },
  signatureBorder: {
    width: '80%',
    borderTop: '1px solid #B0B0B0',
    height: '2',
    textAlign: 'center'
  },
  signatureContainer: {
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center'
  },
  sign: {
    width: '200px',
    height: '90px',
    marginBottom: '-20px'
  },
  footerMarginBottom: {
    marginBottom: 25
  },
  qrCode: {
    width: 100,
    height: 100,
    marginRight: 20
  }
})

const CertificatePDF = ({ certificate, user, name, event, qrCodeSrc, viewPDF = false, alternativeName }: CertificatePDFProps) => {
  type TranslationKey = 'heading' | 'authority' | 'leadStatement' | 'preTitle' | 'signature' | 'footer'
  
  interface Translation {
    locale: string
    [key: string]: string | undefined
  }

  const isValidTranslation = (obj: unknown): obj is Translation => {
    return (
      typeof obj === 'object' && 
      obj !== null && 
      'locale' in obj && 
      typeof (obj as Translation).locale === 'string'
    )
  }

  const getTranslation = (key: TranslationKey, defaultValue: string, locale: string = 'en'): string => {
    if (!certificate.certificateTemplateId && !viewPDF) return defaultValue
    
    const translations = certificate?.certificateTemplate?.translations || []
    const translation = translations.find(t => 
      isValidTranslation(t) && t.locale === locale
    ) as Translation | undefined
    
    return translation?.[key] ?? defaultValue
  }

  return (
    <Document>
      <Page style={styles.page} size={[550, 842]} orientation='landscape'>
        <View style={styles.logoContainer}>
          <Image style={styles.logo} src='/logos.png' />
          {qrCodeSrc && <Image style={styles.qrCode} src={qrCodeSrc} />}
        </View>
        <Text style={styles.certificateAchievementArabic}>
          {getTranslation('heading', 'شـــهــــــــادة اجـتيـــــــاز', 'ar')}
        </Text>
        <Text style={styles.certificateAchievementEnglish}>
          {getTranslation('heading', 'Certificate of Achievement', 'en')}
        </Text>
        <View style={styles.flexDescription}>
          <Text style={styles.subheading}>
            {getTranslation('authority', 'Saudi Data and AI Authority (SDAIA) Certifies that', 'en')}
          </Text>
          <Text style={styles.subheading}>
            {getTranslation('authority', 'تمنح الهيئة السعودية للبيانات والذكاء الاصطناعي (سدايا)', 'ar')}
          </Text>
        </View>
        <View style={(alternativeName || user?.profile?.alternativeName) ? styles.flexDescription : styles.center}>
          <Text style={styles.issuedTo}>{user?.profile?.name ?? name}</Text>
          <Text style={styles.issuedTo}>{alternativeName ?? user?.profile?.alternativeName}</Text>
        </View>
        <View style={styles.flexDescription}>
          <Text style={styles.details1}>
            {getTranslation('leadStatement', 'Has Obtained the Certificate of Achievement for', 'en')}
          </Text>
          <Text style={styles.details1}>
            {getTranslation('leadStatement', 'شــــــــــهــادة اجـــتـــــياز', 'ar')}
          </Text>
        </View>
        <View style={styles.flexDescription}>
          <Text style={styles.title}>
            {getTranslation('preTitle', 'Professional Training Program in', 'en')} 
            &nbsp;
            {event?.translations?.find(item => item.locale === 'en')?.name ?? certificate?.certificationableTranslations?.find(item => item.locale === 'en')?.title ?? certificate?.certificationableTitle} 
          </Text>
          <Text style={styles.titleArabic}>
            {getTranslation('preTitle', 'البرنامج التدريبي الاحترافي في', 'ar')}
            &nbsp;
            {event?.translations?.find(item => item.locale === 'ar')?.name ?? certificate?.certificationableTranslations?.find(item => item.locale === 'ar')?.title ?? certificate?.certificationableTitle}
          </Text>
        </View>
        <View style={styles.flexDescription}>
          <Text style={styles.details}>
            Completion Date: {viewPDF ? 'DD/MM/YYYY' : formatToMonthDayYear(certificate.completionDate as string)}
          </Text>
          <Text style={styles.details}>
            تاريخ الإنجاز: {viewPDF ? 'شهر/يوم/سنة' : formatToMonthDayYear(certificate.completionDate as string)}
          </Text>
        </View>
        <View style={styles.footer}>
          <View style={styles.signatureContainer}>
            <Image style={styles.sign} src='/signature.png' />
            <View style={styles.signatureBorder} />
            <Text style={styles.signature}>
              {getTranslation('footer', 'د أحمد عوض الغامدي | المشرف العام على قطاع بناء القدرات', 'ar')}
            </Text>
            <Text style={styles.signature}>
              {getTranslation('footer', 'Dr. Ahmed Alghamdi | The Head of Capacity Building', 'en')}
            </Text>
          </View>
        </View>
      </Page>
    </Document>
  )
}

export default CertificatePDF

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions