From 4f96a018cdfe2cde17be0dd0ab04ee7f2ca5d0e7 Mon Sep 17 00:00:00 2001
From: THE-CHEMIST
Date: Mon, 27 Apr 2026 18:39:14 +0000
Subject: [PATCH] feat: add QR code generation for vaccination records
- Add QRCodeModal component using qrcode library
- Add 'Show QR' button to each NFTCard
- Wire up PatientDashboard to show modal with verify URL
- QR encodes /verify?wallet=G...&token=TOKEN_ID
- Support PNG download from modal
- Fix pre-existing syntax error in RecordDetailModal
---
frontend/package.json | 9 +--
frontend/src/components/NFTCard.jsx | 16 +++++-
frontend/src/components/QRCodeModal.jsx | 57 +++++++++++++++++++
frontend/src/components/RecordDetailModal.jsx | 2 +-
frontend/src/pages/PatientDashboard.jsx | 17 +++++-
5 files changed, 94 insertions(+), 7 deletions(-)
create mode 100644 frontend/src/components/QRCodeModal.jsx
diff --git a/frontend/package.json b/frontend/package.json
index 6341394..69564a8 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -3,13 +3,14 @@
"version": "1.0.0",
"private": true,
"dependencies": {
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-router-dom": "^6.22.0",
"@stellar/freighter-api": "^2.0.0",
"@stellar/stellar-sdk": "^12.0.0",
"i18next": "^23.11.5",
- "react-i18next": "^14.1.2"
+ "qrcode": "^1.5.4",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-i18next": "^14.1.2",
+ "react-router-dom": "^6.22.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.2.1",
diff --git a/frontend/src/components/NFTCard.jsx b/frontend/src/components/NFTCard.jsx
index a559bfa..d2721e2 100644
--- a/frontend/src/components/NFTCard.jsx
+++ b/frontend/src/components/NFTCard.jsx
@@ -1,6 +1,6 @@
import CopyButton from './CopyButton';
-export default function NFTCard({ record, onClick }) {
+export default function NFTCard({ record, onClick, onShowQR }) {
const { t } = useTranslation();
return (
@@ -39,6 +39,20 @@ export default function NFTCard({ record, onClick }) {
Issuer: {record.issuer?.slice(0, 8)}…{record.issuer?.slice(-4)}
+ {onShowQR && (
+
+ )}
);
}
diff --git a/frontend/src/components/QRCodeModal.jsx b/frontend/src/components/QRCodeModal.jsx
new file mode 100644
index 0000000..930c3d4
--- /dev/null
+++ b/frontend/src/components/QRCodeModal.jsx
@@ -0,0 +1,57 @@
+import { useEffect, useRef } from 'react';
+import QRCode from 'qrcode';
+
+export default function QRCodeModal({ url, onClose }) {
+ const canvasRef = useRef(null);
+
+ useEffect(() => {
+ if (canvasRef.current) {
+ QRCode.toCanvas(canvasRef.current, url, { width: 256, margin: 2 });
+ }
+ }, [url]);
+
+ const handleDownload = () => {
+ const link = document.createElement('a');
+ link.download = 'vaccination-qr.png';
+ link.href = canvasRef.current.toDataURL('image/png');
+ link.click();
+ };
+
+ return (
+
+
e.stopPropagation()}
+ style={{
+ background: '#1e293b', borderRadius: 12, padding: '1.5rem',
+ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1rem',
+ }}
+ >
+
Vaccination QR Code
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/RecordDetailModal.jsx b/frontend/src/components/RecordDetailModal.jsx
index eab7a9f..7a0d20b 100644
--- a/frontend/src/components/RecordDetailModal.jsx
+++ b/frontend/src/components/RecordDetailModal.jsx
@@ -88,7 +88,7 @@ export default function RecordDetailModal({ record, onClose }) {
- ))}
+ )}
)}
- {currentItems.map((r) => )}
+ {currentItems.map((r) => (
+
+ ))}
+
+ {qrRecord && (
+ setQrRecord(null)}
+ />
+ )}
{totalPages > 1 && (