Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Stivenz3/Nexu/llms.txt

Use this file to discover all available pages before exploring further.

Nexu’s certificate download and QR generation are entirely client-side operations. No server function is invoked, and no file is uploaded to Firebase Storage during download. The PDF is built by capturing a live DOM node as an image and embedding it into an A4 landscape document — all inside the user’s browser.

Certificate HTML Template: CertificateDocument

The visual certificate is rendered by src/components/certificate/CertificateDocument.tsx. The root <div> is given a stable DOM ID:
// src/components/certificate/CertificateDocument.tsx
const DOCUMENT_ID = 'nexu-certificate-document'

export { DOCUMENT_ID as CERTIFICATE_DOCUMENT_ID }
CertificateOfficialPage imports this constant and passes it to the PDF download function so the two are always in sync — there is no hardcoded string on the call site. The component renders:
  • Course name, “Certificado de Aprobación” heading, and the Nexu award icon
  • Holder’s full name and document string
  • A table of approved modules (completedLessons) with per-lesson scores
  • Verification code, average score, issue date, and expiry date
  • A signature block referencing Resolución 2674 de 2013
  • The QR code image (generated asynchronously, see below)

PDF Generation: downloadCertificatePdf

// src/lib/certificatePdf.ts
import html2canvas from 'html2canvas'
import { jsPDF } from 'jspdf'

export async function downloadCertificatePdf(
  elementId: string,
  fileName: string
): Promise<void>
How it works:
  1. Looks up document.getElementById(elementId) — throws if the node is absent.
  2. Calls html2canvas(element, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }) to render the DOM node to a <canvas> at 2× resolution.
  3. Converts the canvas to a PNG data URL.
  4. Creates a jsPDF instance with { orientation: 'landscape', unit: 'mm', format: 'a4' }.
  5. Calculates centered placement with an 8 mm margin, preserving the canvas aspect ratio.
  6. Calls pdf.addImage(...) then pdf.save(fileName) to trigger the browser download.
Output filename pattern (assembled in CertificateOfficialPage):
const safeName = certificate.userName.replace(/\s+/g, '_').slice(0, 40)
`Nexu_Certificado_${safeName}_${certificate.verifyCode}.pdf`
// e.g. "Nexu_Certificado_Maria_Lopez_NX-AB12CD34EF.pdf"
Download button lives in the ActionsCard component inside CertificateOfficialPage:
// src/pages/CertificateOfficialPage.tsx
await downloadCertificatePdf(
  CERTIFICATE_DOCUMENT_ID,
  `Nexu_Certificado_${safeName}_${certificate.verifyCode}.pdf`
)

QR Code Generation

The QR is generated with the qrcode npm package directly in the browser. CertificateDocument runs this effect whenever certificate.qrVerifyUrl changes:
// src/components/certificate/CertificateDocument.tsx
useEffect(() => {
  QRCode.toDataURL(certificate.qrVerifyUrl, {
    width: 192,
    margin: 1,
    color: { dark: '#1a1a1a', light: '#ffffff' },
  })
    .then(setQrDataUrl)
    .catch(() => setQrDataUrl(null))
}, [certificate.qrVerifyUrl])
The resulting data URL is rendered as an <img> tag inside the certificate. Because it is part of the DOM at the time html2canvas runs, it is captured in the PDF automatically.

Verification URL: buildVerifyUrl

The QR code encodes a URL assembled by src/lib/verifyUrl.ts:
// src/lib/verifyUrl.ts
export function buildVerifyUrl(verifyCode: string): string {
  const base =
    typeof window !== 'undefined'
      ? window.location.origin
      : import.meta.env.VITE_APP_URL ?? 'https://nexu.vercel.app'
  return `${base.replace(/\/$/, '')}/verificar/${verifyCode}`
}
  • In the browser: uses window.location.origin (the current domain).
  • Outside the browser (SSR/build-time): falls back to the VITE_APP_URL environment variable, then to https://nexu.vercel.app.
The final URL format is:
{origin}/verificar/{verifyCode}
// e.g. https://app.nexu.co/verificar/NX-AB12CD34EF

Certificate Pages

RoutePagePurpose
/certificadoCertificatePageShows course progress, per-lesson competency list, and a link to the official certificate once all 8 lessons are passed
/certificado/documentoCertificateOfficialPageShows CertificateDocument, the PDF download button, and the shareable verification link
CertificatePage uses fetchCourseCertificationProgress and getCourseCertificate to decide what to render. CertificateOfficialPage guards access: if not all lessons are passed, it shows a “Certificado no disponible” state (or a demo download if the first two lessons are passed).

npm Dependencies

PackagePurpose
jspdfCreates the PDF file and handles A4 landscape formatting
html2canvasCaptures the certificate DOM node as a pixel-accurate PNG
qrcodeGenerates the QR code as a data URL image
These are frontend dependencies installed via pnpm and bundled by Vite. They are not used in any Firebase function or server-side code.
Set the VITE_APP_URL environment variable in your Vercel project settings to your production domain (e.g. https://app.nexu.co). Without it, QR codes generated during a server-side or non-browser context will fall back to https://nexu.vercel.app. In normal browser usage window.location.origin is used automatically, but setting this variable ensures consistency across all environments.
Phase 2 — Firebase Storage upload: PDF upload to Firebase Storage is not implemented today. In a future iteration, downloadCertificatePdf (or a Cloud Function) will upload the generated PDF to Firebase Storage and persist the download URL in certificates/{certificateId}. For now, the PDF is generated and downloaded on demand entirely in the browser without any Storage interaction.

Build docs developers (and LLMs) love