From 5be4cd3fd4be55a2c62365c5a56fa8cc412991f2 Mon Sep 17 00:00:00 2001 From: suvanbanerjee Date: Tue, 31 Dec 2024 17:11:34 +0530 Subject: [PATCH] new form --- app/generate/page.tsx | 272 ++++++++++++++++++++++++++++++++ components/ui/morphing-text.tsx | 142 +++++++++++++++++ utils/base62.ts | 37 +++++ 3 files changed, 451 insertions(+) create mode 100644 app/generate/page.tsx create mode 100644 components/ui/morphing-text.tsx create mode 100644 utils/base62.ts diff --git a/app/generate/page.tsx b/app/generate/page.tsx new file mode 100644 index 0000000..f946873 --- /dev/null +++ b/app/generate/page.tsx @@ -0,0 +1,272 @@ +'use client'; + +import React, { useState } from 'react'; +import QRCode from 'qrcode'; +import { PDFDocument, StandardFonts, rgb } from 'pdf-lib'; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +const allergiesList = ['Pollen', 'Dust', 'Pet Dander', 'Peanuts', 'Shellfish'] +const medicationsList = ['Aspirin', 'Ibuprofen', 'Penicillin', 'Insulin', 'Metformin'] +const medicalConditionsList = ['Asthma', 'Diabetes', 'Hypertension', 'Arthritis', 'Migraine'] + +const Generate = () => { + const [formData, setFormData] = useState({ + name: '', + dob: '', + height: '', + weight: '', + bloodGroup: '', + emergencyContact: '', + allergies: [], + substanceUse: '', + pregnant: false, + organDonor: false, + medications: [], + medicalConditions: [], + }); + + const [loading, setLoading] = useState(false); + + const handleInputChange = (e) => { + const { name, value, type, checked } = e.target; + if (type === 'checkbox' && (name === 'allergies' || name === 'medications' || name === 'medicalConditions')) { + setFormData((prevData) => { + const items = new Set(prevData[name]); + if (checked) items.add(value); + else items.delete(value); + return { ...prevData, [name]: Array.from(items) }; + }); + } else if (type === 'checkbox') { + setFormData({ ...formData, [name]: checked }); + } else { + setFormData({ ...formData, [name]: value }); + } + }; + + const handleGenerateQRCode = async () => { + setLoading(true); + const { + name, + dob, + height, + weight, + bloodGroup, + emergencyContact, + allergies, + substanceUse, + pregnant, + organDonor, + } = formData; + + const queryString = new URLSearchParams({ + name, + dob, + height, + weight, + bloodGroup, + emergencyContact, + allergies: allergies.join(','), + substanceUse, + pregnant: pregnant.toString(), + organDonor: organDonor.toString(), + }).toString(); + + const link = `https://opentag.github.io/profile?${queryString}`; + const qrCodeOptions = { + margin: 0, + color: { dark: '#000000', light: '#ffffff' }, + }; + + const qrCodeDataUrl = await QRCode.toDataURL(link, qrCodeOptions); + const existingPdfBytes = await fetch('/template.pdf').then((res) => res.arrayBuffer()); + const pdfDoc = await PDFDocument.load(existingPdfBytes); + const pages = pdfDoc.getPages(); + const firstPage = pages[0]; + const qrImage = await pdfDoc.embedPng(qrCodeDataUrl); + + firstPage.drawImage(qrImage, { x: 40, y: 100, width: 100, height: 100 }); + const font = await pdfDoc.embedFont(StandardFonts.HelveticaBold); + + firstPage.drawText(`Name: ${name}`, { x: 150, y: 400, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`DOB: ${dob}`, { x: 150, y: 380, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`Height: ${height}`, { x: 150, y: 360, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`Weight: ${weight}`, { x: 150, y: 340, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`Blood Group: ${bloodGroup}`, { x: 150, y: 320, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`Emergency Contact: ${emergencyContact}`, { x: 150, y: 300, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`Substance Use: ${substanceUse}`, { x: 150, y: 280, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`Pregnant: ${pregnant ? 'Yes' : 'No'}`, { x: 150, y: 240, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`Organ Donor: ${organDonor ? 'Yes' : 'No'}`, { x: 150, y: 220, size: 12, font, color: rgb(0, 0, 0) }); + firstPage.drawText(`Allergies: ${allergies.join(', ')}`, { + x: 150, + y: 200, + size: 10, + font, + color: rgb(0, 0, 0), + }); + + const pdfBytes = await pdfDoc.save(); + const blob = new Blob([pdfBytes], { type: 'application/pdf' }); + const url = URL.createObjectURL(blob); + const downloadLink = document.createElement('a'); + downloadLink.href = url; + downloadLink.download = `profile.pdf`; + downloadLink.click(); + URL.revokeObjectURL(url); + + setLoading(false); + }; + + return ( +
+
+

+ Get Serverless Tag +

+
+ + + Medical Information Form + + +
{ e.preventDefault(); handleGenerateQRCode(); }}> +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+

Allergies

+
+ {allergiesList.map((allergy) => ( +
+ + +
+ ))} +
+
+
+

Medications

+
+ {medicationsList.map((medication) => ( +
+ + +
+ ))} +
+
+
+

Medical Conditions

+
+ {medicalConditionsList.map((condition) => ( +
+ + +
+ ))} +
+
+
+ +
+
+
+
+
+ ); +}; + +export default Generate; \ No newline at end of file diff --git a/components/ui/morphing-text.tsx b/components/ui/morphing-text.tsx new file mode 100644 index 0000000..e2b8051 --- /dev/null +++ b/components/ui/morphing-text.tsx @@ -0,0 +1,142 @@ +'use client'; + +import { useCallback, useEffect, useRef } from "react"; +import { cn } from "@/lib/utils"; + +const morphTime = 3; // Changed to 3 seconds +const cooldownTime = 1; // Changed to 1 second + +const useMorphingText = (texts: string[]) => { + const textIndexRef = useRef(0); + const morphRef = useRef(0); + const cooldownRef = useRef(0); + const timeRef = useRef(new Date()); + + const text1Ref = useRef(null); + const text2Ref = useRef(null); + + const setStyles = useCallback( + (fraction: number) => { + const [current1, current2] = [text1Ref.current, text2Ref.current]; + if (!current1 || !current2) return; + + current2.style.filter = `blur(${Math.min(8 / fraction - 8, 100)}px)`; + current2.style.opacity = `${Math.pow(fraction, 0.4) * 100}%`; + + const invertedFraction = 1 - fraction; + current1.style.filter = `blur(${Math.min(8 / invertedFraction - 8, 100)}px)`; + current1.style.opacity = `${Math.pow(invertedFraction, 0.4) * 100}%`; + + current1.textContent = texts[textIndexRef.current % texts.length]; + current2.textContent = texts[(textIndexRef.current + 1) % texts.length]; + }, + [texts], + ); + + const doMorph = useCallback(() => { + morphRef.current -= cooldownRef.current; + cooldownRef.current = 0; + + let fraction = morphRef.current / morphTime; + + if (fraction > 1) { + cooldownRef.current = cooldownTime; + fraction = 1; + } + + setStyles(fraction); + + if (fraction === 1) { + textIndexRef.current++; + } + }, [setStyles]); + + const doCooldown = useCallback(() => { + morphRef.current = 0; + const [current1, current2] = [text1Ref.current, text2Ref.current]; + if (current1 && current2) { + current2.style.opacity = "100%"; + current1.style.opacity = "0%"; + } + }, []); + + useEffect(() => { + let animationFrameId: number; + + const animate = () => { + animationFrameId = requestAnimationFrame(animate); + + const newTime = new Date(); + const dt = (newTime.getTime() - timeRef.current.getTime()) / 1000; + timeRef.current = newTime; + + cooldownRef.current -= dt; + + if (cooldownRef.current <= 0) doMorph(); + else doCooldown(); + }; + + animate(); + return () => { + cancelAnimationFrame(animationFrameId); + }; + }, [doMorph, doCooldown]); + + return { text1Ref, text2Ref }; +}; + +interface MorphingTextProps { + className?: string; + texts: string[]; +} + +const Texts: React.FC> = ({ texts }) => { + const { text1Ref, text2Ref } = useMorphingText(texts); + return ( + <> + + + + ); +}; + +const SvgFilters: React.FC = () => ( + + + + + + + +); + +const MorphingText: React.FC = ({ texts, className }) => ( +
+ + +
+); + +export default MorphingText; diff --git a/utils/base62.ts b/utils/base62.ts new file mode 100644 index 0000000..8413d2d --- /dev/null +++ b/utils/base62.ts @@ -0,0 +1,37 @@ +/** + * Converts a binary string to a Base62 encoded string. + * Base62 characters: A-Z, a-z, 0-9 + * + * @param binaryStr - The binary string to convert (e.g., "101011") + * @returns The Base62 encoded string. + */ +function binaryToBase62(binaryStr: string): string { + if (!/^[01]+$/.test(binaryStr)) { + throw new Error("Input must be a binary string (only 0s and 1s)."); + } + + // Define the Base62 character set + const base62Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const base62Radix = 62; + + // Convert binary string to decimal number + const decimalValue = BigInt("0b" + binaryStr); + + // Convert decimal number to Base62 + let base62Str = ""; + let value = decimalValue; + + while (value > 0) { + const remainder = Number(value % BigInt(base62Radix)); + base62Str = base62Chars[remainder] + base62Str; + value = value / BigInt(base62Radix); + } + + // Return the Base62 string ("0" for input "0") + return base62Str || "0"; +} + +// Example usage +const binaryStr = "101011"; +const base62Str = binaryToBase62(binaryStr); +console.log(base62Str);