Student ID Generator for VEO3

Student ID Generator for VEO3

 

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Student ID Card Generator</title>

    <!-- Tailwind CSS CDN -->

    <script src="https://cdn.tailwindcss.com"></script>

    <!-- HTML2Canvas for capturing DOM as image -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>

    <!-- jsPDF for generating PDF -->

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>

    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700;900&family=Inter:wght@400;500;700&display=swap" rel="stylesheet">

    <style>

        /* Custom styles for the ID card to ensure exact dimensions and professional look */

        .id-card-container {

            width: 2.1in; /* Standard vertical ID card width */

            height: 3.4in; /* Standard vertical ID card height */

            box-sizing: border-box; /* Include padding and border in the element's total width and height */

            position: relative; /* For absolute positioning of elements inside */

            overflow: hidden; /* Hide anything that goes outside the card boundaries */

            display: flex; /* Use flexbox for vertical alignment */

            flex-direction: column; /* Stack children vertically */

            align-items: center; /* Center items horizontally */

            justify-content: flex-start; /* Align items to the start (top) */

            padding: 0.35rem; /* Reduced padding for more content space */

            border: 1px solid #e2e8f0; /* Thin light-gray outline */

            font-family: 'Roboto', Arial, sans-serif; /* Clean sans-serif font */

        }


        /* Ensure images inside the card are responsive and fit their containers */

        .id-card-container img {

            max-width: 100%;

            height: auto;

            display: block;

        }


        /* Hide the default file input text */

        input[type="file"] {

            color: transparent;

        }


        /* NEW: Style for the 3D "ghost" buttons */

        .modern-button {

            background-color: transparent; /* No fill color by default */

            color: #4F46E5; /* Text color is the indigo */

            padding: 0.6rem 1.2rem;

            border-radius: 9999px; /* Pill shape */

            cursor: pointer;

            display: inline-block;

            text-align: center;

            font-size: 0.875rem; /* text-sm */

            font-weight: 600; /* semibold */

            transition: all 0.15s ease-out; /* Snappy transition */

            border: 2px solid #4F46E5; /* Visible outline */

            text-transform: uppercase;

            letter-spacing: 0.05em;

            /* 3D "pushed out" effect */

            box-shadow: 0 4px #4338CA; /* Darker indigo shadow for depth */

            position: relative;

            top: 0;

        }


        .modern-button:hover {

            background-color: #4F46E5; /* Fill color appears on hover */

            color: #fff; /* White text on hover */

            /* 3D "pressed" effect */

            top: 2px;

            box-shadow: 0 2px #4338CA; /* Reduce shadow to simulate pressing */

        }

        

        .modern-button:active {

            /* Deeper "pressed" effect on click */

            top: 4px;

            box-shadow: 0 0 #4338CA;

        }


        /* Custom modal for messages */

        .custom-modal {

            position: fixed;

            top: 50%;

            left: 50%;

            transform: translate(-50%, -50%);

            background-color: #fff;

            padding: 20px;

            border-radius: 8px;

            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);

            z-index: 1000;

            text-align: center;

            font-family: 'Inter', sans-serif;

            color: #333;

            display: none; /* Hidden by default */

        }

        

        .custom-modal.show {

            display: block;

        }


        .custom-modal.success {

            border: 2px solid #28a745;

            color: #28a745;

        }


        .custom-modal.error {

            border: 2px solid #dc3545;

            color: #dc3545;

        }


        /* Tricolor divider styles */

        .tricolor-divider {

            display: flex;

            width: 100%;

            height: 2px; /* Thin line */

            margin-top: 0.2rem; /* Space above divider */

            margin-bottom: 0.5rem; /* Space below divider */

        }

        .tricolor-divider > div {

            flex: 1;

            height: 100%;

        }

        .saffron { background-color: #FF9933; } /* Saffron */

        .white { background-color: #FFFFFF; border: 0.5px solid #e2e8f0; } /* White with a faint border */

        .green { background-color: #138808; } /* India Green */


        /* Specific styles for student photo and signature */

        .student-photo-container {

            width: 0.8in; /* Small square photo */

            height: 0.8in;

            background-color: #BFDBFE; /* Blue-100 */

            border-radius: 0.25rem; /* rounded-md */

            display: flex;

            align-items: center;

            justify-content: center;

            overflow: hidden;

            border: 1px solid #9CA3AF; /* Gray-400 border */

        }


        .principal-signature-area {

            text-align: center; /* Centered label */

            line-height: 1; /* Adjust line height for signature text */

            margin-top: 0.2rem; /* Space above signature */

        }

        .principal-signature-area img {

            height: 0.35in; /* Adjust height to fit */

            width: auto;

            max-width: 0.8in; /* Max width for signature */

            object-fit: contain;

            display: inline-block;

            vertical-align: bottom; /* Align with text baseline */

            margin-bottom: 0.1rem; /* Space between image and label */

        }

    </style>

</head>

<body class="font-sans bg-gray-100 min-h-screen flex items-center justify-center p-4">

    <div class="flex flex-col lg:flex-row bg-white rounded-lg shadow-xl overflow-hidden w-full max-w-6xl">

        <!-- Left Panel: Edit Area -->

        <div class="w-full lg:w-1/2 p-6 md:p-8 space-y-6 border-r border-gray-200 overflow-y-auto" style="max-height: 90vh;">

            <h2 class="text-3xl font-bold text-gray-800 mb-6 text-center">ID Card Editor</h2>


            <!-- Upload Options -->

            <div class="space-y-4">

                <label class="block text-sm font-medium text-gray-700">Upload College Logo</label>

                <label for="collegeLogoUpload" class="modern-button">Choose File</label>

                <input type="file" id="collegeLogoUpload" accept="image/*" class="hidden">

            </div>


            <div class="space-y-4">

                <label class="block text-sm font-medium text-gray-700">Upload Student Photo</label>

                <label for="studentPhotoUpload" class="modern-button">Choose File</label>

                <input type="file" id="studentPhotoUpload" accept="image/*" class="hidden">

            </div>


            <div class="space-y-4">

                <label class="block text-sm font-medium text-gray-700">Upload Principal Signature</label>

                <label for="principalSignatureUpload" class="modern-button">Choose File</label>

                <input type="file" id="principalSignatureUpload" accept="image/*" class="hidden">

            </div>


            <!-- Text Fields -->

            <div class="space-y-4">

                <label for="collegeNameInput" class="block text-sm font-medium text-gray-700">College Name:</label>

                <input type="text" id="collegeNameInput" placeholder="N. C. College (Karimganj, Assam)" value="N. C. College (Karimganj, Assam)" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">

            </div>


            <div class="space-y-4">

                <label for="nameInput" class="block text-sm font-medium text-gray-700">Name:</label>

                <input type="text" id="nameInput" placeholder="Anil Kumar" value="Anil Kumar" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">

            </div>


            <div class="space-y-4">

                <label for="classInput" class="block text-sm font-medium text-gray-700">Class:</label>

                <input type="text" id="classInput" placeholder="12" value="12" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">

            </div>


            <div class="space-y-4">

                <label for="rollNumberInput" class="block text-sm font-medium text-gray-700">Roll Number:</label>

                <input type="text" id="rollNumberInput" placeholder="55" value="55" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">

            </div>


            <div class="space-y-4">

                <label for="dobInput" class="block text-sm font-medium text-gray-700">Date of Birth:</label>

                <input type="date" id="dobInput" value="2000-05-09" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">

            </div>


            <div class="space-y-4">

                <label for="yearInput" class="block text-sm font-medium text-gray-700">Year:</label>

                <input type="text" id="yearInput" placeholder="2025" value="2025" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">

            </div>


            <div class="space-y-4">

                <label for="addressInput" class="block text-sm font-medium text-gray-700">Address (for bottom bar):</label>

                <input type="text" id="addressInput" placeholder="N. C. College (Karimganj, Assam)" value="N. C. College (Karimganj, Assam)" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">

            </div>


            <div class="space-y-4">

                <label for="mobileInput" class="block text-sm font-medium text-gray-700">Mobile Number (for bottom bar):</label>

                <input type="tel" id="mobileInput" placeholder="9162830269" value="9162830269" class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">

            </div>

        </div>


        <!-- Right Panel: Live Card View -->

        <div class="w-full lg:w-1/2 p-6 md:p-8 flex flex-col items-center justify-center bg-gray-50 relative">

            <h2 class="text-3xl font-bold text-gray-800 mb-6 text-center">Live ID Card Preview</h2>


            <!-- ID Card Preview Area -->

            <div id="idCardPreview" class="id-card-container bg-white rounded-lg shadow-lg text-black relative">

                <!-- Top Section: Logo and College Name -->

                <div class="flex items-center w-full mb-1">

                    <img id="cardLogo" src="https://placehold.co/50x50/E0E7FF/4F46E5?text=Logo" alt="College Logo" class="w-12 h-12 rounded-full object-cover mr-2 border-2 border-gray-200 flex-shrink-0" onerror="this.onerror=null;this.src='https://placehold.co/50x50/E0E7FF/4F46E5?text=Logo';">

                    <h1 id="cardCollegeName" class="text-xs font-extrabold text-center flex-grow">N. C. College (Karimganj, Assam)</h1>

                </div>


                <!-- Tricolor Divider Line -->

                <div class="tricolor-divider">

                    <div class="saffron"></div>

                    <div class="white"></div>

                    <div class="green"></div>

                </div>


                <!-- Main Content Area (Student Info + Photo/Signature) -->

                <div class="flex w-full flex-grow px-1.5 pb-1 justify-between items-start">

                    <!-- Student Information (Left-Aligned) -->

                    <div class="flex flex-col text-[0.6rem] space-y-0.5 flex-grow pr-1">

                        <p><span class="font-semibold">Name:</span> <span id="cardName"></span></p>

                        <p><span class="font-semibold">Class:</span> <span id="cardClass"></span></p>

                        <p><span class="font-semibold">Roll No.:</span> <span id="cardRollNumber"></span></p>

                        <p><span class="font-semibold">D.O.B:</span> <span id="cardDob"></span></p>

                        <p><span class="font-semibold">Year:</span> <span id="cardYear"></span></p>

                    </div>


                    <!-- Photo and Signature Section (Right-Aligned) -->

                    <div class="flex flex-col items-center justify-start h-full pl-1">

                        <!-- Student Photo -->

                        <div class="student-photo-container mb-1">

                            <img id="cardStudentPhoto" src="https://placehold.co/80x80/E0E7FF/4F46E5?text=Photo" alt="Student Photo" class="w-full h-full object-cover" onerror="this.onerror=null;this.src='https://placehold.co/80x80/E0E7FF/4F46E5?text=Photo';">

                        </div>

                        <p class="text-gray-600 text-[0.55rem] text-center">Student Photo</p>


                        <!-- Principal Signature Block -->

                        <div class="principal-signature-area mt-auto">

                            <img id="cardPrincipalSignature" src="https://placehold.co/100x40/E0E7FF/4F46E5?text=Signature" alt="Principal Signature" onerror="this.onerror=null;this.src='https://placehold.co/100x40/E0E7FF/4F46E5?text=Signature';">

                            <p class="text-[0.5rem] font-bold">Principal Signature</p> <!-- Changed label and made bold -->

                        </div>

                    </div>

                </div>


                <!-- Bottom Navy-Blue Strip -->

                <div class="absolute bottom-0 left-0 w-full bg-blue-900 text-white text-[0.5rem] py-1 px-1 font-medium flex justify-center items-center text-center">

                    <span id="cardAddressMobile"></span>

                </div>

            </div>


            <!-- Download Button -->

            <button id="downloadCardButton" class="modern-button mt-8">

                Download Card

            </button>

        </div>

    </div>


    <script>

        // Function to show a custom modal message

        function showMessage(message, type = 'info') {

            const existingModal = document.querySelector('.custom-modal');

            if (existingModal) {

                existingModal.remove();

            }


            const modal = document.createElement('div');

            modal.className = `custom-modal ${type}`;

            modal.textContent = message;

            document.body.appendChild(modal);

            

            // Use a timeout to apply the 'show' class to trigger transition

            setTimeout(() => {

                modal.classList.add('show');

            }, 10);



            setTimeout(() => {

                modal.classList.remove('show');

                setTimeout(() => modal.remove(), 500); // Remove from DOM after transition

            }, 3000); // Remove after 3 seconds

        }


        // Function to handle image uploads and update the card preview

        function handleImageUpload(event, targetElementId) {

            const file = event.target.files[0];

            if (file) {

                const reader = new FileReader();

                reader.onload = function(e) {

                    const imgElement = document.getElementById(targetElementId);

                    imgElement.src = e.target.result;

                };

                reader.readAsDataURL(file);

            }

        }


        // Function to update text fields on the card preview

        function updateCardText(inputId, cardElementId) {

            const inputElement = document.getElementById(inputId);

            const cardElement = document.getElementById(cardElementId);


            // Set initial value

            cardElement.textContent = inputElement.value;


            // Add event listener for real-time updates

            inputElement.addEventListener('input', () => {

                cardElement.textContent = inputElement.value;

            });

        }


        // Function to update the combined address and mobile in the bottom bar

        function updateCombinedAddressMobile() {

            const address = document.getElementById('addressInput').value;

            const mobile = document.getElementById('mobileInput').value;

            document.getElementById('cardAddressMobile').textContent = `Address: ${address} — Mobile: ${mobile}`;

        }



        // Initialize all text fields and image uploads

        document.addEventListener('DOMContentLoaded', () => {

            // Image upload listeners

            document.getElementById('collegeLogoUpload').addEventListener('change', (e) => handleImageUpload(e, 'cardLogo'));

            document.getElementById('studentPhotoUpload').addEventListener('change', (e) => handleImageUpload(e, 'cardStudentPhoto'));

            document.getElementById('principalSignatureUpload').addEventListener('change', (e) => handleImageUpload(e, 'cardPrincipalSignature'));


            // Text field listeners

            updateCardText('collegeNameInput', 'cardCollegeName');

            updateCardText('nameInput', 'cardName');

            updateCardText('classInput', 'cardClass');

            updateCardText('rollNumberInput', 'cardRollNumber');

            updateCardText('dobInput', 'cardDob');

            updateCardText('yearInput', 'cardYear');

            

            // Initial update for combined address/mobile and add listeners

            updateCombinedAddressMobile();

            document.getElementById('addressInput').addEventListener('input', updateCombinedAddressMobile);

            document.getElementById('mobileInput').addEventListener('input', updateCombinedAddressMobile);

        });



        // Download functionality

        document.getElementById('downloadCardButton').addEventListener('click', async () => {

            const card = document.getElementById('idCardPreview');


            // We need to capture at a higher resolution for 300 DPI output.

            // The card is 2.1in x 3.4in. At 300 DPI, this is 630px x 1020px.

            // We use html2canvas's scale option for this.

            const scaleFactor = 4; // Capture at 4x the rendered resolution for better quality


            try {

                // Show a loading message

                showMessage("Generating your card...", "info");


                // Capture the card with html2canvas

                const canvas = await html2canvas(card, {

                    scale: scaleFactor,

                    useCORS: true, // Important for images, especially from data URLs or external sources

                    logging: false,

                    width: card.offsetWidth,

                    height: card.offsetHeight,

                    scrollX: -window.scrollX,

                    scrollY: -window.scrollY,

                    windowWidth: document.documentElement.offsetWidth,

                    windowHeight: document.documentElement.offsetHeight

                });


                const studentName = document.getElementById('nameInput').value.replace(/ /g, '_') || 'student';


                // --- PNG Download ---

                const pngUrl = canvas.toDataURL('image/png');

                const pngLink = document.createElement('a');

                pngLink.href = pngUrl;

                pngLink.download = `${studentName}_id_card.png`;

                document.body.appendChild(pngLink);

                pngLink.click();

                document.body.removeChild(pngLink);

                


                // --- PDF Download ---

                const { jsPDF } = window.jspdf;

                // ID card dimensions in mm: 2.1 inches = 53.34 mm, 3.4 inches = 86.36 mm

                const pdfWidth = 53.34;

                const pdfHeight = 86.36;


                // Create a new jsPDF instance with custom dimensions

                const pdf = new jsPDF({

                    orientation: 'portrait',

                    unit: 'mm',

                    format: [pdfWidth, pdfHeight]

                });


                const imgData = canvas.toDataURL('image/png');

                // Add the image to the PDF, covering the entire page

                pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);

                pdf.save(`${studentName}_id_card.pdf`);

                

                // Show success message after a short delay

                setTimeout(() => {

                     showMessage("Card downloaded as PNG and PDF!", "success");

                }, 500);



            } catch (error) {

                console.error("Error generating card:", error);

                showMessage("Failed to generate card. Check console for errors.", "error");

            }

        });

    </script>

</body>

</html>



Post a Comment

Previous Post Next Post