import React, { useState, useRef, useEffect } from 'react'; import { Camera, Printer, Upload, FileText, ImageIcon, Palette, Image as BgImageIcon, Scaling, PenTool, MoveVertical, Layout, Type, User, RectangleHorizontal, Hash, MapPin, List, ShieldCheck, Stamp, MoveHorizontal, Maximize, Eye, Circle, Edit3, Wand2, Star, Calendar, Download, Database, Save, RotateCcw, Trash2, FileJson, UploadCloud, FileSpreadsheet, FileType, Globe, FileText as FileWord, Lock, Unlock, QrCode } from 'lucide-react'; export default function App() { const [activeTab, setActiveTab] = useState('front'); const [isLibLoaded, setIsLibLoaded] = useState(false); const [isExcelLoaded, setIsExcelLoaded] = useState(false); const [isPdfLoaded, setIsPdfLoaded] = useState(false); const [isDocxLoaded, setIsDocxLoaded] = useState(false); // LOCK STATES const [isLocked, setIsLocked] = useState(false); const [isLabelLocked, setIsLabelLocked] = useState(false); const [isBackFooterLocked, setIsBackFooterLocked] = useState(false); const [isChannelNameLocked, setIsChannelNameLocked] = useState(false); const [isHeaderLocked, setIsHeaderLocked] = useState(false); // --- DEFAULT VALUES --- const defaultDetails = { // --- Front Side Data --- headerText: 'PRESS', pressHeaderSize: '42', pressHeaderTop: '8', pressTextColor: '#D32F2F', pressBgColor: '#ffffff', pressBorderColor: '#000000', pressBorderWidth: '0', channelName: 'JAMMU BHARAT NEWS', name: 'MOHAN LAL', designation: 'Editor-in-Chief', phone: '7006710843', validUntil: '2025-12-31', bloodGroup: 'B+', rniNo: 'DELHIN/2025/64915', idNo: 'JBN-RNI-2025-001', franchiseText: 'A FRANCHISE OF LEAD INDIA', agencyText: 'NATIONAL NEWS AGENCY & NEWS PAPER', sigTitle: "Holder's Signature", bottomStripText: 'PRESS DIGITAL MEDIA', // Channel Name Strip Defaults channelNameBgColor: '#D32F2F', channelNameTextColor: '#FFFFFF', channelNameVerticalShift: '0', // --- Labels (Manual Edit) --- labelId: 'ID No.', labelName: 'Name', labelDesig: 'Designation', labelPhone: 'Phone no.', labelValid: 'Valid upto', labelBlood: 'Blood Group', labelRni: 'RNI NO.', // --- Labels Back (Manual Edit) --- labelBackRni: 'RNI NO.', labelInstruction: 'INSTRUCTION', labelOfficeAddr: 'Office Address:', // --- Styles --- bgColor: '#ffffff', // Borders outerBorderColor: '#000000', innerBorderColor: '#000000', // Left Logo Styles logoSize: '40', logoTop: '0', logoLeft: '0', logo1Brightness: '100', logo1Contrast: '100', logo1Grayscale: '0', logo1BorderWidth: '0', logo1BorderColor: '#000000', // Right Logo Styles logo2Size: '40', logo2Top: '0', logo2Right: '0', logo2Brightness: '100', logo2Contrast: '100', logo2Grayscale: '0', logo2BorderWidth: '0', logo2BorderColor: '#000000', logoTouchBorder: false, contentTop: '0', mainTextColor: '#000000', channelNameSize: '24', photoSize: '110', isPhotoRound: false, photoBorderColor: '#000000', bottomStripBgColor: '#D32F2F', bottomStripTextColor: '#FFFFFF', bottomStripSize: '24', rniSize: '14', sigSize: '40', sigLabelSize: '10', sigLabelColor: '#000000', sigLineWidth: '128', authSigSize: '40', // Text Sizes detailsTextSize: '14', franchiseSize: '10', agencySize: '10', // Label Specific Styles labelColumnWidth: '110', labelFontSize: '14', labelColor: '#000000', cardBorderColor: '#000000', // Agency Strip Styles agencyBgColor: '#000000', agencyTextColor: '#FFFFFF', // Back Header Styles backHeaderBgColor: '#ffffff', backHeaderTextColor: '#D32F2F', backHeaderTop: '0', // --- Back Side Data --- backChannelName: 'JAMMU BHARAT NEWS', backRniNo: 'DELHIN/2025/64915', backAgencyText: 'NATIONAL NEWS AGENCY & NEWS PAPER', website: 'www.jammubharatnews.com', officeAddress: 'PRESS MEDIA: 118, SARAR, CHENANI, UDHAMPUR, J&K - 182161', backBottomStripText: '', backBottomStripSize: '24', backBottomStripBgColor: '#D32F2F', backBottomStripTextColor: '#FFFFFF', addressSize: '12', addressImgSize: '60', term1: 'Unauthorized use of this card is offence.', term2: 'Card holder is responsible for any kind of misuse of this card.', term3: 'Card can be verified on www.jammubharatnews.com', term4: 'In case this card is lost please acknowledge in nearest Police Station as well as your office.', term5: '', term6: '', authorityName: 'Chief Editor', instructionsTop: '0', instructionsTextColor: '#000000', // --- Auto Stamp Data --- isAutoStamp: false, stampText: 'JAMMU BHARAT NEWS', stampBottomText: 'RNI: DELHIN/2025/12345', stampSubText: 'AUTHORIZED', stampColor: '#D32F2F', showStampStars: true, // --- QR Code Settings --- isQrCodeFront: false, isQrCodeBack: false, qrCodeData: '', qrSize: '60', qrFrontTop: '360', qrFrontLeft: '280', qrBackTop: '360', qrBackLeft: '280' }; const [details, setDetails] = useState(defaultDetails); const [savedRecords, setSavedRecords] = useState([]); useEffect(() => { try { const saved = localStorage.getItem('jbn_current_draft_details_v30'); if(saved) setDetails(JSON.parse(saved)); else setDetails(defaultDetails); const loadedData = localStorage.getItem('jbn_saved_cards'); if (loadedData) setSavedRecords(JSON.parse(loadedData)); } catch(e) { console.error(e); } const loadScript = (src, onload) => { const script = document.createElement('script'); script.src = src; script.async = true; script.onload = onload; document.body.appendChild(script); return script; }; const s1 = loadScript('https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js', () => setIsLibLoaded(true)); const s2 = loadScript('https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js', () => setIsExcelLoaded(true)); const s3 = loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js', () => setIsPdfLoaded(true)); const s4 = loadScript('https://unpkg.com/docx@7.1.0/build/index.js', () => setIsDocxLoaded(true)); return () => { if(document.body.contains(s1)) document.body.removeChild(s1); if(document.body.contains(s2)) document.body.removeChild(s2); if(document.body.contains(s3)) document.body.removeChild(s3); if(document.body.contains(s4)) document.body.removeChild(s4); }; }, []); const [personImage, setPersonImage] = useState(localStorage.getItem('jbn_img_person') || null); const [logoImage, setLogoImage] = useState(localStorage.getItem('jbn_img_logo1') || null); const [logo2Image, setLogo2Image] = useState(localStorage.getItem('jbn_img_logo2') || null); const [bgLogo, setBgLogo] = useState(localStorage.getItem('jbn_img_bg') || null); const [bgBackImage, setBgBackImage] = useState(localStorage.getItem('jbn_img_bg_back') || null); const [signatureImage, setSignatureImage] = useState(localStorage.getItem('jbn_img_sig') || null); const [stampImage, setStampImage] = useState(localStorage.getItem('jbn_img_stamp') || null); const [addressImage, setAddressImage] = useState(localStorage.getItem('jbn_img_address') || null); const [authoritySigImage, setAuthoritySigImage] = useState(localStorage.getItem('jbn_img_auth') || null); const personInputRef = useRef(null); const logoInputRef = useRef(null); const logo2InputRef = useRef(null); const bgLogoInputRef = useRef(null); const bgBackInputRef = useRef(null); const signatureInputRef = useRef(null); const stampInputRef = useRef(null); const addressImageRef = useRef(null); const authoritySigInputRef = useRef(null); const formatDate = (dateStr) => { if (!dateStr) return ''; const date = new Date(dateStr); if (isNaN(date.getTime())) return dateStr; return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' }); }; const handleInputChange = (e) => { if (isLocked) return; const { name, value, type, checked } = e.target; setDetails(prev => ({ ...prev, [name]: type === 'checkbox' ? checked : value })); }; const handleFrontLanguageChange = (e) => {}; const handleBackLanguageChange = (e) => {}; const handleImageUpload = (e, setImageFunc) => { if (isLocked) return; const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { setImageFunc(reader.result); }; reader.readAsDataURL(file); } }; const handleSaveRecord = () => { const record = { id: Date.now(), date: new Date().toLocaleString(), data: details, images: { personImage, logoImage, logo2Image, bgLogo, bgBackImage, signatureImage, stampImage, addressImage, authoritySigImage } }; const updatedList = [record, ...savedRecords]; setSavedRecords(updatedList); localStorage.setItem('jbn_saved_cards', JSON.stringify(updatedList)); alert("Record Saved Successfully!"); }; const handleClearForm = () => { if(window.confirm("Reset form to original MOHAN LAL data?")) { setDetails(defaultDetails); localStorage.removeItem('jbn_current_draft_details_v30'); alert("Form Reset!"); } }; const handleDeleteRecord = (id) => { if(window.confirm("Delete this record?")) { const updatedList = savedRecords.filter(rec => rec.id !== id); setSavedRecords(updatedList); localStorage.setItem('jbn_saved_cards', JSON.stringify(updatedList)); } }; const handleLoadRecord = (record) => { if(window.confirm("Load this record? Current form data will be replaced.")) { setDetails(record.data || defaultDetails); setPersonImage(record.images?.personImage || null); setLogoImage(record.images?.logoImage || null); setLogo2Image(record.images?.logo2Image || null); setBgLogo(record.images?.bgLogo || null); setBgBackImage(record.images?.bgBackImage || null); setSignatureImage(record.images?.signatureImage || null); setStampImage(record.images?.stampImage || null); setAddressImage(record.images?.addressImage || null); setAuthoritySigImage(record.images?.authoritySigImage || null); setActiveTab('front'); } }; const handleClearAllRecords = () => { if(window.confirm("WARNING: This will delete ALL saved records. Are you sure?")) { setSavedRecords([]); localStorage.removeItem('jbn_saved_cards'); } }; const captureElement = async (elementId) => { const element = document.getElementById(elementId); if (!element) return null; const originalDisplay = element.style.display; if (originalDisplay === 'none') element.style.display = 'block'; try { const canvas = await window.html2canvas(element, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }); if (originalDisplay === 'none') element.style.display = 'none'; return canvas.toDataURL('image/png'); } catch (e) { console.error(e); if (originalDisplay === 'none') element.style.display = 'none'; return null; } }; const handleExportWord = async () => { if (!isDocxLoaded || !window.docx || !window.html2canvas) { alert("Word generator loading... please wait."); return; } const frontDataUrl = await captureElement('id-card-front'); const backDataUrl = await captureElement('id-card-back'); if (!frontDataUrl || !backDataUrl) return alert("Error capturing cards."); const { Document, Packer, Paragraph, ImageRun, TextRun, AlignmentType } = window.docx; const doc = new Document({ sections: [{ properties: {}, children: [ new Paragraph({ children: [ new TextRun({ text: "Jammu Bharat News - Press ID Card", bold: true, size: 28 }) ], alignment: AlignmentType.CENTER, spacing: { after: 400 }, }), new Paragraph({ children: [ new ImageRun({ data: frontDataUrl, transformation: { width: 250, height: 400 } }), new TextRun({ text: "\t" }), new ImageRun({ data: backDataUrl, transformation: { width: 250, height: 400 } }), ], alignment: AlignmentType.CENTER, }), ], }], }); const blob = await Packer.toBlob(doc); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = "JBN_ID_Card_A4.docx"; link.click(); }; const handleDownloadDatabase = () => { const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(savedRecords, null, 2)); const downloadAnchorNode = document.createElement('a'); downloadAnchorNode.setAttribute("href", dataStr); downloadAnchorNode.setAttribute("download", "jbn_records.json"); document.body.appendChild(downloadAnchorNode); downloadAnchorNode.click(); downloadAnchorNode.remove(); }; const handleExportExcel = () => { if (!isExcelLoaded || !window.XLSX) return alert("Excel tool loading..."); if (savedRecords.length === 0) return alert("No records."); const dataToExport = savedRecords.map(rec => ({ Date: rec.date, ID: rec.data?.idNo, Name: rec.data?.name, Designation: rec.data?.designation, Phone: rec.data?.phone })); const ws = window.XLSX.utils.json_to_sheet(dataToExport); const wb = window.XLSX.utils.book_new(); window.XLSX.utils.book_append_sheet(wb, ws, "ID Cards"); window.XLSX.writeFile(wb, "JBN_ID_Records.xlsx"); }; const handleExportPDF = () => { if (!isPdfLoaded || !window.jspdf) return alert("PDF tool loading..."); if (savedRecords.length === 0) return alert("No records."); const { jsPDF } = window.jspdf; const doc = new jsPDF(); doc.text("Jammu Bharat News - Records", 14, 20); let y = 30; savedRecords.forEach((rec, i) => { doc.text(`${i + 1}. ${rec.data?.name} | ${rec.data?.designation} | ${rec.data?.idNo}`, 14, y); y += 10; }); doc.save("JBN_List.pdf"); }; const handleSaveImage = async (side) => { if (!isLibLoaded || !window.html2canvas) return alert("Loading..."); const url = await captureElement(side === 'front' ? 'id-card-front' : 'id-card-back'); if(url) { const link = document.createElement('a'); link.download = `JBN-${side}.png`; link.href = url; link.click(); } }; const handleSaveCardPDF = async (side) => { if (!isPdfLoaded || !window.jspdf) return alert("Loading..."); const elementId = side === 'front' ? 'id-card-front' : 'id-card-back'; const element = document.getElementById(elementId); if (!element) return; const wasHidden = element.style.display === 'none'; if(wasHidden) element.style.display = 'block'; const canvas = await window.html2canvas(element, { scale: 3, useCORS: true, backgroundColor: '#ffffff' }); if(wasHidden) element.style.display = 'none'; const url = canvas.toDataURL('image/jpeg', 1.0); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: [54, 86] }); pdf.addImage(url, 'JPEG', 0, 0, 54, 86); pdf.save(`JBN-${side}.pdf`); }; // AUTO SAVE IMAGES useEffect(() => { if(personImage) localStorage.setItem('jbn_img_person', personImage); }, [personImage]); useEffect(() => { if(logoImage) localStorage.setItem('jbn_img_logo1', logoImage); }, [logoImage]); useEffect(() => { if(logo2Image) localStorage.setItem('jbn_img_logo2', logo2Image); }, [logo2Image]); useEffect(() => { if(bgLogo) localStorage.setItem('jbn_img_bg', bgLogo); }, [bgLogo]); useEffect(() => { if(bgBackImage) localStorage.setItem('jbn_img_bg_back', bgBackImage); }, [bgBackImage]); useEffect(() => { if(signatureImage) localStorage.setItem('jbn_img_sig', signatureImage); }, [signatureImage]); useEffect(() => { if(stampImage) localStorage.setItem('jbn_img_stamp', stampImage); }, [stampImage]); useEffect(() => { if(addressImage) localStorage.setItem('jbn_img_address', addressImage); }, [addressImage]); useEffect(() => { if(authoritySigImage) localStorage.setItem('jbn_img_auth', authoritySigImage); }, [authoritySigImage]); // Auto-save details useEffect(() => { localStorage.setItem('jbn_current_draft_details_v30', JSON.stringify(details)); }, [details]); // Helper for QR URL const qrData = details.qrCodeData || `ID:${details.idNo}|${details.website}`; const qrCodeUrl = `https://api.qrserver.com/v1/create-qr-code/?data=${encodeURIComponent(qrData)}&size=150x150`; return (
{/* --- FORM SECTION --- */}
{/* Header & Tabs */}

PRESS ID CARD

{/* MASTER LOCK BUTTON */}
{/* === FRONT SIDE FORM === */} {activeTab === 'front' && (
{/* Front Language Selector */}
{/* Image Uploads Grid */}
!isLocked && personInputRef.current.click()}> {personImage ? Person : }Photo * handleImageUpload(e, setPersonImage)} className="hidden" accept="image/*" />
!isLocked && logoInputRef.current.click()}> {logoImage ? Logo : }Left Logo handleImageUpload(e, setLogoImage)} className="hidden" accept="image/*" />
!isLocked && logo2InputRef.current.click()}> {logo2Image ? Logo 2 : }Right Logo handleImageUpload(e, setLogo2Image)} className="hidden" accept="image/*" />
!isLocked && bgLogoInputRef.current.click()}> {bgLogo ? Bg : }Front Bg handleImageUpload(e, setBgLogo)} className="hidden" accept="image/*" />
!isLocked && signatureInputRef.current.click()}> {signatureImage ? Sig : }Signature handleImageUpload(e, setSignatureImage)} className="hidden" accept="image/*" />
{/* Front Inputs */}
{/* MAIN HEADER SETTINGS - UPGRADED */}

Main Header Settings

{/* CHANNEL NAME STRIP SETTINGS */}

Channel Name Strip

{/* AGENCY STRIP SETTINGS */}

Agency Strip Settings

{/* SIGNATURE LABEL INPUT & SETTINGS */}
{/* NEW: Sig Label Color & Size Controls */}
{/* FRONT FOOTER SETTINGS SECTION */}

Front Footer Strip Settings

{/* QR CODE SETTINGS (FRONT) */}
{details.isQrCodeFront && (
)}
{/* LABEL EDITOR SECTION - UPGRADED */}

Manually Edit Labels

{/* LABEL LOCK BUTTON */}

Label Styles (Color & Size)

{/* Style Settings (With Logo Color Grading) */}

Style Settings

{/* NEW: Border Color Settings */}

Border Line Colors

{/* Logo Controls */}

Left Logo

Right Logo

)} {/* === BACK SIDE FORM === */} {activeTab === 'back' && (
{/* ... Back Side Controls ... */}

Company Details