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 }) {

- ))} + )}
{explorerUrl ? ( { @@ -73,7 +75,20 @@ export default function PatientDashboard() {
)} - {currentItems.map((r) => )} + {currentItems.map((r) => ( + + ))} + + {qrRecord && ( + setQrRecord(null)} + /> + )} {totalPages > 1 && (