From f61ef39cd1a7b1e477f2848197f02bd638c115c2 Mon Sep 17 00:00:00 2001 From: mayankyadav19 Date: Sat, 6 Jun 2026 11:04:56 +0530 Subject: [PATCH] feat: implement-qr-generation-for-class-invite-link --- package-lock.json | 17 ++++++ package.json | 2 + src/app/rooms/[id]/page.tsx | 117 +++++++++++++++++++++++++++++++++--- 3 files changed, 129 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index d06de1a8..dffa931e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "framer-motion": "^12.40.0", "google-tts-api": "^0.0.6", "groq-sdk": "1.1.1", + "html-to-image": "^1.11.13", "inngest": "^4.4.0", "input-otp": "1.4.2", "jspdf": "^4.2.1", @@ -67,6 +68,7 @@ "next-themes": "0.4.6", "pdf-parse": "2.4.5", "pdf-parse-fork": "1.2.0", + "qrcode.react": "^4.2.0", "react": "18.3.1", "react-day-picker": "9.4.1", "react-dom": "18.3.1", @@ -15622,6 +15624,12 @@ "dev": true, "license": "MIT" }, + "node_modules/html-to-image": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", + "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==", + "license": "MIT" + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -20928,6 +20936,15 @@ ], "license": "MIT" }, + "node_modules/qrcode.react": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz", + "integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/qs": { "version": "6.15.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", diff --git a/package.json b/package.json index 3f9881b0..381381c3 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "framer-motion": "^12.40.0", "google-tts-api": "^0.0.6", "groq-sdk": "1.1.1", + "html-to-image": "^1.11.13", "inngest": "^4.4.0", "input-otp": "1.4.2", "jspdf": "^4.2.1", @@ -70,6 +71,7 @@ "next-themes": "0.4.6", "pdf-parse": "2.4.5", "pdf-parse-fork": "1.2.0", + "qrcode.react": "^4.2.0", "react": "18.3.1", "react-day-picker": "9.4.1", "react-dom": "18.3.1", diff --git a/src/app/rooms/[id]/page.tsx b/src/app/rooms/[id]/page.tsx index dc9ea460..d1028e41 100644 --- a/src/app/rooms/[id]/page.tsx +++ b/src/app/rooms/[id]/page.tsx @@ -9,6 +9,7 @@ import { import { useEffect, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { useAppUser } from "../../provider"; +import { QRCodeCanvas } from "qrcode.react"; import { Brain, MessageSquare, @@ -324,6 +325,21 @@ export default function ClassroomPage() { } }; + const downloadQr = () => { + const canvas = document.getElementById( + "invite-qr" + ) as HTMLCanvasElement; + + if (!canvas) return; + + const url = canvas.toDataURL("image/png"); + + const link = document.createElement("a"); + link.href = url; + link.download = "classroom-invite-qr.png"; + link.click(); + }; + const copyInviteLink = async () => { if (!inviteUrl) return; @@ -1019,13 +1035,100 @@ export default function ClassroomPage() { -
- {/* Current Invite Code */} -
-

- Current Invite Code -

- +
+
+
+ +
+

+ Secure Invite Link +

+

+ This creates a non-guessable invite link that expires in 7 + days. Students can use it to join this classroom directly. +

+
+
+
+ + + + {inviteUrl && ( +
+ {/* Left side */} +
+
+

+ Invite link +

+ +
+ + + +
+
+ + {inviteExpiresAt && ( +

+ Expires on {new Date(inviteExpiresAt).toLocaleString()} +

+ )} +
+ + {/* Right side */} +
+
+ +
+ + +
+
+ )} +
+ +
+

+ Existing class code fallback +

+ +
+ {classroom?.inviteCode}