import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Messages } from '../../Components/Notifications/Messages'; import FormHeader from '../PageComponents/FormHeader'; import { Tables } from '../../Components/Tables/Table'; import { DropDowns } from '../../Components/Forms/DropDown'; import { useDispatch } from 'react-redux'; import { getConfiguration, PublicQrCodepost, } from '../../Features/ConfigMasterPage/ConfigMasterPage'; import Buttons from '../../Components/Forms/Buttons'; import { PlusOutlined, DownloadOutlined, EyeOutlined } from '@ant-design/icons'; import { MdClear } from 'react-icons/md'; import { getDineinTableData } from '../../Features/Dinein/Dinein'; import { encryptObject, getSession } from '../../Services/Others'; import QRCode from 'react-qr-code'; import jsPDF from 'jspdf'; import html2canvas from 'html2canvas'; import { Modal } from 'antd'; import { ApplicationPreferences, getBranchDetail, } from '../../Features/BrachLogin/BranchLogin'; import { changeBreadCrumb, getEmpAccess } from '../../Features/AppPage/CenterPage'; import { useSelector } from 'react-redux'; import PublicQrCodeShare from './PublicQrCodeShare/PublicQrCodeShare'; import { FeatureAddon } from '../../Features/BookingScreen/BookingData/BookingData'; import { getPaymentOptionsData } from '../../Features/Payment/Paymentoptions/Paymentoptions'; import { useAuth } from '../../AuthContext'; const subDirectory = import.meta.env.ENV_MAIN_BASE_URL; const items = [ { name: 'Home', link: `${subDirectory}app-page/home`, }, ]; const PublicQrCode = () => { const dispatch = useDispatch(); console.log('PublicQrCode component rendered', `${subDirectory}`); const appPreferences = useSelector(ApplicationPreferences); const commonModulePreference = appPreferences?.find( (preference) => preference?.PreferredCatName === 'Common Module' )?.PreferenceCatDetails; const sportsAppPreference = commonModulePreference?.find( (preference) => preference?.PreferredSubCatName === 'SportsApp' && preference?.PreferredStatus == 'Y' ); const [messageType, setMessageType] = useState(null); const [messageData, setMessageData] = useState(null); const [configdatas, setConfigdatas] = useState([]); const [configId, setConfigId] = useState(null); const [TableId, setTableId] = useState(null); const [WaiterId, setWaiterId] = useState(null); const [Tabledata, setTabledata] = useState([]); const [CheckTabledata, setCheckTabledata] = useState([]); const [qrCodeUrl, setQrCodeUrl] = useState(''); const [empData, setEmpData] = useState(); const [addnewAccess, setaddnewAccess] = useState(true); const [selectedConfig, setSelectedConfig] = useState({ id: '', name: 'Both', }); const [SelectedSelfBooking, setSelectedSelfBooking] = useState({ id: '', name: '', }); const initializedRef = useRef(false); const configIdInitializedRef = useRef(false); const handleSubmitCalledRef = useRef(false); const [branchData, setBranchData] = useState([]); const [page, setPage] = useState(1); const [isPreviewModalOpen, setIsPreviewModalOpen] = useState(false); const [pdfPreviewUrl, setPdfPreviewUrl] = useState(''); const qrCodeRef = useRef(null); const CompId = getSession('CompId'); const BranchId = getSession('BranchId'); const AppId = getSession('AppId'); const UserId = getSession('UserId'); const UserType = getSession('UserType'); const { SadminuserAccess } = useAuth(); const SAAccessCommonMaster = SadminuserAccess?.find( (e) => e?.MenuName === 'SelfBooking QR Code' ); const allowed = ['TakeAway', CheckTabledata?.length > 0 ? 'Dine In' : '']; const bookingTypePreference = appPreferences?.find( (preference) => preference?.PreferredCatName === 'Booking Type' )?.PreferenceCatDetails; const dinePreference = bookingTypePreference?.find( (type) => type?.PreferredSubCatName?.toLowerCase() === 'dine in' && type?.PreferredStatus === 'Y' ); const takeAwayPreference = bookingTypePreference?.find( (type) => type?.PreferredSubCatName?.toLowerCase() === 'take away' && type?.PreferredStatus === 'Y' ); const filtered = configdatas.filter( (item) => allowed.includes(item.ConfigName) && item?.TypeName == 'Booking Type' ); const SelfBooking = configdatas.filter( (item) => item?.TypeName === 'SelfBookingOption' ); const SelfBookingtype = configdatas.filter( (item) => item?.TypeName === 'SelfBookingType' ); // Check if filtered contains both "Dine In" and "TakeAway" const hasDineIn = filtered.some( (item) => item.ConfigName?.toLowerCase() === 'dine in' ); const hasTakeAway = filtered.some( (item) => item.ConfigName?.toLowerCase() === 'takeaway' ); const hasBoth = hasDineIn && hasTakeAway; let finalFiltered = []; filtered.forEach((item) => { const name = item.ConfigName?.toLowerCase(); if (sportsAppPreference) { if (name === 'takeaway') { finalFiltered.push({ ...item, ConfigName: 'Online Booking' }); } } else { if (name === 'dine in') { if (dinePreference) { finalFiltered.push(item); // keep dine in } } else if (name === 'takeaway') { if (takeAwayPreference) { finalFiltered.push(item); // keep take away } else { // rename to Sales if takeaway not allowed finalFiltered.push({ ...item, ConfigName: 'Sales' }); } } else { finalFiltered.push(item); // keep any other booking type } } }); // Remove duplicates by ConfigName finalFiltered = finalFiltered.filter( (item, index, self) => index === self.findIndex((t) => t.ConfigName === item.ConfigName) ); // Add "Both" option only if filtered contains both "Dine In" and "TakeAway" and sportsAppPreference is false if (hasBoth && !sportsAppPreference) { finalFiltered.unshift({ ConfigId: 'All', // you can give a special id ConfigName: 'Both', }); } console.log(finalFiltered, filtered, 'Final Filtered'); useEffect(() => { fetchApi(); fetchBranchData(); dispatch(changeBreadCrumb({ items: items })); }, []); // Reset initialization refs when CheckTabledata or sportsAppPreference changes useEffect(() => { configIdInitializedRef.current = false; }, [CheckTabledata]); useEffect(() => { handleSubmitCalledRef.current = false; }, [sportsAppPreference]); // Set default configId based on available options useEffect(() => { if (configIdInitializedRef.current || !finalFiltered?.length) return; if (sportsAppPreference) { // Set default to "Online Booking" when sportsAppPreference is true const onlineBookingOption = finalFiltered.find( (option) => option.ConfigName === 'Online Booking' ); if (onlineBookingOption) { setConfigId(onlineBookingOption.ConfigId); configIdInitializedRef.current = true; } } else if (hasBoth) { // Set default to "Both" if both options are available setConfigId('All'); configIdInitializedRef.current = true; } else if (finalFiltered?.length > 0) { // Set default to first available option if only one is available setConfigId(finalFiltered[0]?.ConfigId || null); configIdInitializedRef.current = true; } }, [sportsAppPreference, finalFiltered, hasBoth]); const fetchBranchData = async () => { const response = await dispatch( getBranchDetail({ BrId: BranchId }) ).unwrap(); if (response?.data?.statusCode === 1) { setBranchData(response?.data?.data); } else { setBranchData(); } let response2 = await dispatch( getDineinTableData({ CompId: CompId, BranchId: BranchId, AppId: AppId }) ).unwrap(); if (response2?.data?.statusCode == 1) { setCheckTabledata(response2?.data?.data); } }; useEffect(() => { let hasAccess = false; if (UserType === 'Admin' || UserType === 'Super Admin') { hasAccess = true; } else if (UserType === 'Employee') { hasAccess = empData?.AddAccess === 'Y'; } else if (UserType === 'Super Admin User') { hasAccess = SAAccessCommonMaster?.AddAccess === 'Y'; } setaddnewAccess(!hasAccess); }, [empData, SAAccessCommonMaster, UserType]); useEffect(() => { if (UserType === "Employee") { fetchApiEmpAccess() } }, [UserType]) const fetchApiEmpAccess = async () => { let data = { CompId: CompId, BranchId: BranchId, AppId: AppId, EmpId: UserId, }; let response = await dispatch(getEmpAccess(data)).unwrap(); let datas = response?.data?.data?.[0]?.EmpAccessDetails?.filter( (item) => item.ConfigName === 'SelfBooking QR Code' ); setEmpData(datas?.[0]); }; const fetchApi = async () => { let response = await dispatch(getConfiguration({ Type: 'SA' })).unwrap(); if (response?.data?.statusCode === 1) { setConfigdatas(response?.data?.data); } }; const ConfigSelect = (e) => { let isDinein = filtered?.find((item) => item.ConfigId === e)?.ConfigName === 'Dine In'; if (isDinein && !sportsAppPreference) { TableData(); } else { setTabledata([]); } setConfigId(e); setQrCodeUrl(''); // Clear QR code when booking type changes setTableId(null); setWaiterId(null); // Reset table selection }; const TableSelect = (e) => { let getWaiterId = Tabledata?.find((a) => a?.TableId == e); setTableId(e); setWaiterId(getWaiterId?.WaiterId); setQrCodeUrl(''); // Clear QR code when table changes }; const TableData = async () => { let response = await dispatch( getDineinTableData({ CompId: CompId, BranchId: BranchId, AppId: AppId }) ).unwrap(); if (response?.data?.statusCode == 1) { setTabledata(response?.data?.data); } }; const HandleSubmit = async () => { if (!sportsAppPreference) { try { const bookingTypeName = filtered?.find((item) => item.ConfigId === configId)?.ConfigName || 'Both'; const tableName = Tabledata?.find((item) => item.TableId === TableId)?.TableName || ''; console.log(bookingTypeName, 'bookingTypeName'); if (!configId) { setMessageType('error'); setMessageData('Please select a booking type.'); return; } let data = {}; if (Tabledata?.length > 0) { if (!TableId) { setMessageType('error'); setMessageData('Please select a table.'); return; } data = { BranchId, BookingType: configId, TableId, bookingTypeName, tableName, WaiterId, selfBookingOption: selectedConfig.name, selfBookingtype: SelectedSelfBooking.name, }; } else { data = { BranchId, BookingType: configId, bookingTypeName, selfBookingOption: selectedConfig.name, selfBookingtype: SelectedSelfBooking.name, }; } const encrypted = encryptObject(data); const postData = { Url: encrypted, CreatedBy: UserId }; const res = await dispatch(PublicQrCodepost(postData)).unwrap(); if (res?.data?.statusCode == 1) { const QrUrl = `${subDirectory}selfBooking/${res?.data?.ReferenceNo}`; setQrCodeUrl(QrUrl); generatePDF(true, QrUrl); } } catch (error) { console.error('HandleSubmit Error:', error); setMessageType('error'); setMessageData('An unexpected error occurred.'); } } else { let Data1 = filtered?.find((e) => e?.ConfigName === 'TakeAway'); let data = { BranchId, BookingType: Data1?.ConfigId, bookingTypeName: Data1?.ConfigName, selfBookingOption: selectedConfig.name, selfBookingtype: SelectedSelfBooking.name, }; const encrypted = encryptObject(data); const postData = { Url: encrypted, CreatedBy: UserId }; const res = await dispatch(PublicQrCodepost(postData)).unwrap(); if (res?.data?.statusCode == 1) { const QrUrl = `${subDirectory}selfBooking/${res?.data?.ReferenceNo}`; setQrCodeUrl(QrUrl); generatePDF(true, QrUrl); } } }; // Auto-submit when sportsAppPreference is true (only once) useEffect(() => { if ( sportsAppPreference && !handleSubmitCalledRef.current && finalFiltered?.length > 0 && configId ) { const onlineBookingOption = finalFiltered.find( (option) => option.ConfigName === 'Online Booking' ); if (onlineBookingOption && configId === onlineBookingOption.ConfigId) { handleSubmitCalledRef.current = true; // Use setTimeout to avoid calling during render setTimeout(() => { HandleSubmit(); }, 0); } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [sportsAppPreference, finalFiltered, configId]); // Function to generate PDF const generatePDF = async (isPreview = false, encrypted) => { console.log('generatePDF called with:', { isPreview, encrypted }); if (!encrypted) { setMessageType('error'); setMessageData('Please generate QR code first'); return; } try { // Get booking type name and table name for display const bookingTypeName = filtered?.find((item) => item.ConfigId === configId)?.ConfigName || 'Both'; const tableName = Tabledata?.find((item) => item.TableId === TableId)?.TableName || ''; // Get branch details const branch = branchData?.[0] || {}; const branchName = branch.BrName || branch.CompName || ''; const branchMobile = branch.BrMobile || ''; const companyLogo = branch.CompLogo || ''; const branchAddress = [ branch.Address1, branch.Address2, branch.City, branch.State, branch.Zip, ] .filter(Boolean) .join(', ') || ''; // Create a temporary div for PDF content const tempDiv = document.createElement('div'); tempDiv.style.padding = '40px'; tempDiv.style.backgroundColor = 'white'; tempDiv.style.fontFamily = 'Arial, sans-serif'; tempDiv.style.textAlign = 'center'; tempDiv.style.width = '600px'; tempDiv.style.margin = '0 auto'; // Add content to the temporary div tempDiv.innerHTML = `
${ companyLogo ? `
` : '' }

${branchName || ''}

${branchAddress || ''}

${branchMobile || ''}


Scan & Book Easily

🔗 ${encrypted}

`; // Add the temporary div to the document document.body.appendChild(tempDiv); // Create QR code in the container const qrContainer = tempDiv.querySelector('#qr-code-container'); const qrCodeSvg = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); qrCodeSvg.setAttribute('width', '200'); qrCodeSvg.setAttribute('height', '200'); qrCodeSvg.setAttribute('viewBox', '0 0 256 256'); // Use the QRCode library to generate SVG content const QRCodeLib = await import('qrcode'); const qrDataUrl = await QRCodeLib.toDataURL(encrypted, { width: 200, margin: 2, }); // Create an image element for the QR code const qrImage = document.createElement('img'); qrImage.src = qrDataUrl; qrImage.style.width = '200px'; qrImage.style.height = '200px'; qrContainer.appendChild(qrImage); // Wait for all images to load (QR code and company logo if present) const imagesToLoad = [qrImage]; const companyLogoImg = tempDiv.querySelector('#company-logo'); if (companyLogoImg) { imagesToLoad.push(companyLogoImg); } await Promise.all( imagesToLoad.map( (img) => new Promise((resolve) => { if (img.complete) { resolve(); } else { img.onload = resolve; img.onerror = resolve; // Continue even if logo fails to load } }) ) ); // Generate PDF using html2canvas and jsPDF const canvas = await html2canvas(tempDiv, { scale: 2, useCORS: true, allowTaint: true, backgroundColor: '#ffffff', }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF('p', 'mm', 'a4'); const imgWidth = 190; const pageHeight = 295; const imgHeight = (canvas.height * imgWidth) / canvas.width; let heightLeft = imgHeight; let position = 10; pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight); heightLeft -= pageHeight; while (heightLeft >= 0) { position = heightLeft - imgHeight; pdf.addPage(); pdf.addImage(imgData, 'PNG', 10, position, imgWidth, imgHeight); heightLeft -= pageHeight; } // Clean up document.body.removeChild(tempDiv); if (isPreview) { // Generate blob URL for preview console.log('Generating PDF preview...'); const pdfBlob = pdf.output('blob'); const pdfUrl = URL.createObjectURL(pdfBlob); console.log('PDF preview URL created:', pdfUrl); setPdfPreviewUrl(pdfUrl); setIsPreviewModalOpen(true); } else { // Download the PDF console.log('Downloading PDF...'); const branch = branchData?.[0] || {}; const branchName = ( branch.BrName || branch.CompName || 'Branch' ).replace(/[^a-zA-Z0-9]/g, '_'); const fileName = `QR_Code_${branchName}_${bookingTypeName}_${tableName}_${new Date().toISOString().split('T')[0]}.pdf`; console.log('PDF filename:', fileName); pdf.save(fileName); console.log('PDF download initiated'); setMessageType('success'); setMessageData('PDF downloaded successfully!'); } } catch (error) { console.error('Error generating PDF:', error); console.error('Error details:', { message: error.message, stack: error.stack, encrypted: encrypted, configId: configId, TableId: TableId, }); setMessageType('error'); setMessageData( `Error generating PDF: ${error.message}. Please try again.` ); } }; const onComplete = useCallback(() => { setMessageData(null); setMessageType(null); }, []); const selfSelect = (selectedValue) => { const selectedOption = SelfBooking.find( (option) => option.ConfigId === selectedValue ); if (selectedOption) { setSelectedConfig({ id: selectedOption.ConfigId, name: selectedOption.ConfigName, }); } }; useEffect(() => { if (!initializedRef.current && sportsAppPreference && SelfBooking?.length) { const bothOption = SelfBooking.find( (option) => option.ConfigName === 'Both' ); if (bothOption) { selfSelect(bothOption.ConfigId); initializedRef.current = true; // prevents infinite loop } } }, [sportsAppPreference, SelfBooking]); // const selfBookingType = (selectedValue) => { // const selectedOption = SelfBookingtype.find( // (option) => option.ConfigId === selectedValue // ); // if (selectedOption) { // setSelectedSelfBooking({ // id: selectedOption.ConfigId, // name: selectedOption.ConfigName, // }); // } // }; const selfBookingType = (selectedValue) => { const selectedOption = SelfBookingtype.find( (option) => option.ConfigId === selectedValue ); if (selectedOption) { setSelectedSelfBooking({ id: selectedOption.ConfigId, name: selectedOption.ConfigName, }); } }; useEffect(() => { if ( sportsAppPreference && SelfBookingtype?.length && !SelectedSelfBooking ) { const inShopOption = SelfBookingtype.find( (option) => option.ConfigName === 'In Shop' ); if (inShopOption) { setSelectedSelfBooking({ id: inShopOption.ConfigId, name: inShopOption.ConfigName, }); } } }, [sportsAppPreference, SelfBookingtype, SelectedSelfBooking]); return (
{!sportsAppPreference && ( ({ value: option.ConfigId, label: option.ConfigName, })), ]} defaultValue={configId} valueData={configId} label="Booking Type" onChangeFunction={(e) => ConfigSelect(e)} isOnchanges={configId !== null ? true : false} className="field-DropDown" /> )} {Tabledata?.length > 0 && !sportsAppPreference && ( ({ value: option.TableId, label: option.TableName, })), ]} defaultValue={TableId} label="Table Name" onChangeFunction={(e) => TableSelect(e)} isOnchanges={TableId ? true : false} className="field-DropDown" /> )} {!sportsAppPreference && ( ({ value: option.ConfigId, label: option.ConfigName, })) || [] } defaultValue={ SelfBooking?.find((option) => option.ConfigName === 'Both') ?.ConfigId || 'Both' } label="Screen Type" onChangeFunction={selfSelect} isOnchanges={SelfBooking !== null ? true : false} className="field-DropDown" /> )} {!sportsAppPreference && ( option.ConfigName === 'In Shop' || option.ConfigName === 'Public' )?.map((option) => ({ value: option.ConfigId, label: option.ConfigName, })) || [] } defaultValue={ SelfBookingtype?.find( (option) => option.ConfigName === 'In Shop' )?.ConfigId } label="Self Booking" onChangeFunction={selfBookingType} isOnchanges={SelfBookingtype !== null} className="field-DropDown" /> )} {!sportsAppPreference && (
} > GENERATE
)} {qrCodeUrl && ( <> {!sportsAppPreference && (
{ setQrCodeUrl(''); setConfigId(null); setTableId(null); setWaiterId(null); setTabledata([]); setPdfPreviewUrl(''); setIsPreviewModalOpen(false); }} icon={} > CLEAR
)} )}
{qrCodeUrl && (

Generated QR Code

URL: {qrCodeUrl}

)} {qrCodeUrl && SelectedSelfBooking.name === 'Public' && ( )}
{/* PDF Preview Modal */} { setIsPreviewModalOpen(false); if (pdfPreviewUrl) { URL.revokeObjectURL(pdfPreviewUrl); setPdfPreviewUrl(''); } }} footer={false} // footer={[ // { // generatePDF(false, qrCodeUrl); // setIsPreviewModalOpen(false); // if (pdfPreviewUrl) { // URL.revokeObjectURL(pdfPreviewUrl); // setPdfPreviewUrl(''); // } // }} // icon={} // > // DOWNLOAD // , // { // setIsPreviewModalOpen(false); // if (pdfPreviewUrl) { // URL.revokeObjectURL(pdfPreviewUrl); // setPdfPreviewUrl(''); // } // }} // > // CLOSE // // ]} width={1000} style={{ top: 20 }} > {pdfPreviewUrl && (