Android_Retail/src/Pages/paymentpdfPage/LinkUrlCreate.jsx

660 lines
24 KiB
React
Raw Normal View History

2026-01-27 18:27:29 +05:30
// InvoiceDetail.jsx
import React, { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import moment from 'moment';
import {
dateFormatChange1,
decryptToObject,
encryptObject,
extractLastNumber,
extractLastNumberOrderId,
getSession,
sessionStore,
} from '../../Services/Others';
import { ApplicationPreferences, getBranchDetail, getCommonAppPreference } from '../../Features/BrachLogin/BranchLogin';
import PrintStyle1 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle1';
import PrintStyle2 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle2';
import PrintStyle3 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle3';
import PrintStyle4 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle4';
import PrintStyle5 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle5';
import PrintStyle6 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle6';
import PrintStyle7 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle7';
import PrintStyle8 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle8';
import PrintStyle9 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle9';
import PrintStyle10 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle10';
import PrintStyle11 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle11';
import PrintStyle12 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle12';
import {
getPrintSelectionComponentData,
GloabalFieldDetails,
GlobalPricingAppPricingName,
GlobalprintDatas,
PricingAppPricingNameData,
SelectedPrintTemplate,
} from '../../Features/ThemeChange/ThemeChange';
import {
getPreferenceData,
PaymentGatewayGetdetail,
} from '../../Features/BookingScreen/BookingData/BookingData';
import axios from 'axios';
import { FiDownload, FiChevronLeft, FiChevronRight } from 'react-icons/fi';
import './PaymentPdfBooking.scss';
import TaxInvoice from '../BookingScreen/PrintTemplates/A4/TaxInvoice';
import A4Standard from '../PurchaseReturn/PrintReturn/A4purchaseReturn/A4Standard';
import { PublicQrCodeget, PublicQrCodepost } from '../../Features/ConfigMasterPage/ConfigMasterPage';
import PrintStyle13 from '../BookingScreen/PrintTemplates/ThermalPrinter/PrintStyle13';
import PrintA5Style11 from '../BookingScreen/PrintTemplates/A4/PrintStyleA5';
import InvoiceTemplate from '../BookingScreen/PrintTemplates/A4/A4Standard';
import PrintA4Style11 from '../BookingScreen/PrintTemplates/A4/PrintA4Style11';
2026-03-24 12:45:51 +05:30
import html2canvas from "html2canvas";
import jsPDF from "jspdf";
2026-01-27 18:27:29 +05:30
const apiUrlToken = import.meta.env.ENV_API_URL_TOKEN;
const LinkUrlCreate = () => {
const dispatch = useDispatch();
const CreatedDate = '2023-10-01T12:00:00Z';
const printRef = useRef();
const containerRef = useRef();
const [currentPage, setCurrentPage] = useState(0);
const url_string = window.location.href;
const url = new URL(url_string);
const [PrintOrderDetails, setPrintOrderDetails] = useState([]);
const [printerTemplateStyle, setPrintSettingData] = useState('');
const [Preference, setPreferenceData] = useState([]);
const [base64Image, setBase64Image] = useState(null);
const [conversionError, setConversionError] = useState(null);
const [allData, setAllData] = useState(null);
const [table2Data, setTable2Data] = useState({});
const [branchDetails, setBranchDetails] = useState({});
const [ProdId, setProdId] = useState([]);
const [isDataLoading, setIsDataLoading] = useState(true);
const [allDataLoaded, setAllDataLoaded] = useState(false);
const codesParam = url.searchParams.get('code');
// Global selectors
const FieldDetails = useSelector(GloabalFieldDetails);
const pricingAppName = useSelector(GlobalPricingAppPricingName);
const printTemplate = useSelector(SelectedPrintTemplate); // This should get the PrintTemplate from global state
const globalPrintDatas = useSelector(GlobalprintDatas); // This should get printDatas from global state
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"
);
useEffect(() => {
initializeData();
}, []);
const initializeData = async () => {
try {
setIsDataLoading(true);
setAllDataLoaded(false);
await addSession();
await getQrCode();
} catch (error) {
console.error('Error initializing data:', error);
setIsDataLoading(false);
}
};
const getQrCode = async () => {
try {
const getQrCodedata = await dispatch(PublicQrCodeget(codesParam)).unwrap();
const oldUrl = getQrCodedata.data.data?.[0]?.OldUrl;
const decrypted = decryptToObject(oldUrl);
// Normalize decrypted value into an array of OrderIds
let orderIds = [];
if (Array.isArray(decrypted)) {
orderIds = decrypted.map((v) => String(v).trim()).filter(Boolean);
} else if (typeof decrypted === 'string') {
orderIds = decrypted.split(',').map((v) => v.trim()).filter(Boolean);
} else if (decrypted && typeof decrypted === 'object') {
const possible = decrypted.OrderId || decrypted.orderId || decrypted.code || decrypted.id;
if (possible) orderIds = [String(possible).trim()];
}
setProdId(orderIds);
} catch (error) {
console.error('Error getting QR code:', error);
setIsDataLoading(false);
}
};
const addSession = async () => {
2026-03-24 12:45:51 +05:30
if (!getSession('auth')) {
2026-01-27 18:27:29 +05:30
try {
let data = {
username: '1000000001',
password: '1234',
};
const response = await axios.post(
`${apiUrlToken}/jwtTokenGenerator`,
data,
{
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
}
);
const { token } = response?.data;
2026-03-24 12:45:51 +05:30
sessionStore('auth', token);
2026-01-27 18:27:29 +05:30
sessionStore('LoginType', 'Kiosk');
} catch (error) {
console.error('Error adding session:', error);
}
}
};
// Fetch API data when ProdId is available
useEffect(() => {
if (Array.isArray(ProdId) && ProdId.length > 0) {
fetchApiData(ProdId);
}
}, [ProdId]);
const fetchApiData = async (orderIds = []) => {
try {
const promises = orderIds.map((orderId) =>
dispatch(PaymentGatewayGetdetail(orderId))
.unwrap()
.then((response) => response?.data?.data?.[0] || null)
);
const results = await Promise.all(promises);
const validResults = results.filter(Boolean);
setPrintOrderDetails(validResults);
if (validResults.length > 0) {
await loadAllRequiredData(validResults[0]);
}
} catch (error) {
console.error("Error fetching bill details:", error);
setIsDataLoading(false);
}
};
const loadAllRequiredData = async (firstInvoice) => {
try {
const { AppId, CompId, BranchId,UserId } = firstInvoice;
2026-01-27 18:27:29 +05:30
const pricingData = {
appId: AppId,
compId: CompId,
branchId: BranchId,
UserId : UserId
2026-01-27 18:27:29 +05:30
};
const printData = { AppId, CompId, BranchId, UserId };
2026-01-27 18:27:29 +05:30
// Step 1: Call PricingAppPricingName to update global state
console.log("Step 1: Calling PricingAppPricingNameData...");
const pricingRes = await dispatch(PricingAppPricingNameData(pricingData)).unwrap();
console.log(pricingRes, "PricingAppPricingName response");
const commonapppreference = await dispatch(getCommonAppPreference({ appId: AppId})).unwrap();
// Step 2: Call getPrintSelectionComponentData to update global state with print template
console.log("Step 2: Calling getPrintSelectionComponentData...");
const printSettings = await dispatch(getPrintSelectionComponentData(printData)).unwrap();
console.log(printSettings,commonapppreference, "getPrintSelectionComponentData response");
// Step 3: Call getPreferenceData
console.log("Step 3: Calling getPreferenceData...");
const preferences = await dispatch(getPreferenceData(printData)).unwrap();
setPreferenceData(preferences?.data);
// Step 4: Fetch branch details
console.log("Step 4: Fetching branch details...");
const branchIds = [...new Set([BranchId])]; // Start with current branch
await Promise.all(branchIds.map(branchId => fetchBranchData(branchId)));
// All data loaded successfully
console.log("All data loaded successfully");
setAllDataLoaded(true);
} catch (error) {
console.error('Error loading required data:', error);
} finally {
setIsDataLoading(false);
}
};
// Handle additional branch details after PrintOrderDetails is set
useEffect(() => {
if (PrintOrderDetails?.length > 0 && allDataLoaded) {
// Get any additional unique branch IDs from all invoices
const allBranchIds = [...new Set(PrintOrderDetails.map(invoice => invoice.BranchId))];
const currentBranchIds = Object.keys(branchDetails);
const newBranchIds = allBranchIds.filter(id => !currentBranchIds.includes(String(id)));
// Fetch details for any new branches
newBranchIds.forEach(branchId => {
if (branchId) {
fetchBranchData(branchId);
}
});
}
}, [PrintOrderDetails, allDataLoaded]);
const fetchBranchData = async (BranchId) => {
try {
const response = await dispatch(
getBranchDetail({ BrId: BranchId })
).unwrap();
if (response?.data?.statusCode === 1) {
setBranchDetails(prev => ({
...prev,
[BranchId]: {
address: response?.data?.data?.[0]?.Address1,
city: response?.data?.data?.[0]?.City,
zip: response?.data?.data?.[0]?.Zip,
logo: response?.data?.data?.[0]?.CompLogo
}
}));
}
} catch (error) {
console.error('Error fetching branch data:', error);
}
};
const imageUrlToBase64 = async (url) => {
try {
const response = await fetch(url);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
} catch (error) {
console.error('Error fetching or converting image:', error);
throw error;
}
};
// Convert logos to base64
useEffect(() => {
async function convertLogos() {
const newBase64Images = {};
for (const [branchId, details] of Object.entries(branchDetails)) {
if (details.logo) {
try {
const base64 = await imageUrlToBase64(details.logo);
newBase64Images[branchId] = base64;
} catch (error) {
console.error(`Error converting image for branch ${branchId}:`, error);
newBase64Images[branchId] = null;
}
}
}
setBase64Image(newBase64Images);
}
if (Object.keys(branchDetails).length > 0) {
convertLogos();
}
}, [branchDetails]);
const CashierName = PrintOrderDetails?.[0]?.CashierName || null;
// const handleDownloadPDF = () => {
// html2pdf().from(printRef.current).save();
// };
2026-03-24 12:45:51 +05:30
// const handleDownloadPDF = () => {
// const element = printRef.current;
// const options = {
// // margin: 0.5,
// margin: 0,
// filename: "document.pdf",
// image: { type: "jpeg", quality: 0.98 },
// html2canvas: { scale: 2, useCORS: true }, // makes CSS + images sharper
// // jsPDF: { unit: "in", format: "a4", orientation: "portrait" },
// jsPDF: { unit: "px", format: [element.offsetWidth, element.offsetHeight] },
// };
// html2pdf().set(options).from(element).save();
// };
const handleDownloadPDF = async () => {
const element = printRef.current;
const canvas = await html2canvas(element, {
scale: 2,
useCORS: true
});
const imgData = canvas.toDataURL("image/jpeg", 0.98);
2026-01-27 18:27:29 +05:30
2026-03-24 12:45:51 +05:30
const pdf = new jsPDF({
unit: "px",
format: [canvas.width, canvas.height],
});
2026-01-27 18:27:29 +05:30
2026-03-24 12:45:51 +05:30
pdf.addImage(imgData, "JPEG", 0, 0, canvas.width, canvas.height);
pdf.save("document.pdf");
};
2026-01-27 18:27:29 +05:30
const scrollToPage = (pageIndex) => {
const pages = containerRef.current?.children;
if (pages && pages[pageIndex]) {
pages[pageIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
setCurrentPage(pageIndex);
}
};
const handleNextPage = () => {
if (currentPage < PrintOrderDetails.length - 1) {
scrollToPage(currentPage + 1);
}
};
const handlePrevPage = () => {
if (currentPage > 0) {
scrollToPage(currentPage - 1);
}
};
// Get preference data
let SalesModePref = Preference?.[0]?.SettingDtlDetails?.filter(
(i) => i.SettingIdName === 'Sales Mode'
)?.[0];
let PrintLogo = Preference?.[0]?.SettingDtlDetails?.filter(
(i) => i.SettingIdName === 'PrintLogo'
)?.[0];
let PrintGreeting = Preference?.[0]?.SettingDtlDetails?.filter(
(i) => i.SettingIdName === 'PrintGreetings'
)?.[0];
const Date1 = dateFormatChange1(CreatedDate);
// Show loading state while data is being fetched
if (isDataLoading || !allDataLoaded) {
return (
<div className="loading-container" style={{ padding: '20px', textAlign: 'center' }}>
<div>Loading invoice Bill...</div>
</div>
);
}
// Show message if no data
if (!PrintOrderDetails || PrintOrderDetails.length === 0) {
return (
<div className="no-data-container" style={{ padding: '20px', textAlign: 'center' }}>
<div>No invoice data found.</div>
</div>
);
}
// Show message if print template is not loaded
if (!printTemplate) {
return (
<div className="no-template-container" style={{ padding: '20px', textAlign: 'center' }}>
<div>Print template not available. Please check your configuration.</div>
</div>
);
}
return (
<>
<div className="invoice-viewer-controls">
<button
onClick={handleDownloadPDF}
className="control-btn download-btn"
title="Download PDF"
>
<FiDownload size={20} />
</button>
{PrintOrderDetails.length > 1 && (
<div className="pagination-controls">
<button
onClick={handlePrevPage}
disabled={currentPage === 0}
className="control-btn nav-btn"
title="Previous Invoice"
>
<FiChevronLeft size={20} />
</button>
<span className="page-indicator">
{currentPage + 1} / {PrintOrderDetails.length}
</span>
<button
onClick={handleNextPage}
disabled={currentPage === PrintOrderDetails.length - 1}
className="control-btn nav-btn"
title="Next Invoice"
>
<FiChevronRight size={20} />
</button>
</div>
)}
</div>
<div className="invoice-container" ref={containerRef}>
<div ref={printRef}>
{PrintOrderDetails?.map((table2Data, index) => {
if (!table2Data) return null;
const { BranchId, SalesDate, CashierName, productDetails, TaxDetails,
ExtraChargeDetails, OrderOfferDetails, PaymentOrderDtl, OrderId, FYStatus } = table2Data;
const branchDetail = branchDetails[BranchId] || {};
const currentBase64Image = base64Image?.[BranchId] || null;
const Date1 = dateFormatChange1(SalesDate);
const PaymentStatusSuccess = PaymentOrderDtl?.filter(ps => ps.PaymentStatus === 'S');
const ExtraChargeTotal = ExtraChargeDetails?.reduce(
(accumulator, currentObject) => accumulator + currentObject.TotalAmt, 0
) || 0;
const productsData = productDetails || [];
const offers = OrderOfferDetails || [];
let TotalOfferAmount = 0;
// Calculate SalesWiseOffers total amount
offers.forEach((offer) => {
if (offer.TableName === 'SalesWiseOffers') {
TotalOfferAmount += offer.OfferAmount;
}
});
const hasMappedOffer = offers.some((offer) =>
['ItemWiseOffers', 'BundleOffers', 'QuantityWiseOffers'].includes(offer.TableName)
);
if (hasMappedOffer) {
const productTotal = productsData?.reduce(
(acc, item) => acc + (item?.Type != 'C' ? item.OfferAmt : item.OfferValue || 0), 0
);
TotalOfferAmount += productTotal;
} else {
const Combothere = productsData?.filter((item) => item?.Type == 'C');
const CombothereTotal = Combothere?.reduce(
(acc, item) => acc + (item.OfferValue || 0), 0
);
TotalOfferAmount += CombothereTotal;
}
let totalQuantityFilter = productsData?.map(
(item) => item?.SalesQty || item?.OrderQty
);
const totalQuantity = totalQuantityFilter?.reduce(
(accumulator, currentValue) => accumulator + currentValue, 0
) || 0;
const OrderDetailGST = TaxDetails?.filter(
(item) => item.TaxIdName?.toLowerCase() === 'cgst,sgst'
);
const OrderDetailIGST = TaxDetails?.filter(
(item) => item.TaxIdName?.toLowerCase() === 'igst'
);
const OrderDetailVAT = TaxDetails?.filter(
(item) => item.TaxIdName?.toLowerCase() === 'vat'
);
const tokenGroups = (productsData || []).reduce(
(acc, item, idx) => {
if (item.TokenAvailable === 'Y') {
if (!item.CounterName) {
acc[`individual-${idx}`] = [item];
} else {
if (!acc[item.CounterName]) acc[item.CounterName] = [];
acc[item.CounterName].push(item);
}
}
return acc;
},
{}
);
// Render print template based on global state
const renderPrintTemplate = () => {
const commonProps = {
index,
ExtraChargeTotal,
Date1,
base64Image: currentBase64Image,
BranchAddress: branchDetail.address,
BranchCity: branchDetail.city,
BranchZip: branchDetail.zip,
SalesModePref,
CashierName,
totalQuantity,
OrderDetailGST,
OrderDetailIGST,
OrderDetailVAT,
table2Data,
totalQuantityFilter,
PaymentStatus: PaymentOrderDtl,
PrintLogo,
PrintGreeting,
printDatas: globalPrintDatas,
sportsAppPreference:sportsAppPreference
};
console.log(printTemplate,"printTemplate")
switch (printTemplate) {
case 'Style 1':
return <PrintStyle1 {...commonProps} isPdf={true} />;
case 'Style 2':
return <PrintStyle2 {...commonProps} isPdf={true} />;
case 'Style 3':
return <PrintStyle3 {...commonProps} isPdf={true} />;
case 'Style 4':
return <PrintStyle4 {...commonProps} isPdf={true} />;
case 'Style 5':
return <PrintStyle5 {...commonProps} isPdf={true} />;
case 'Style 6':
return <PrintStyle6 {...commonProps} isPdf={true} />;
case 'Style 7':
return <PrintStyle7 {...commonProps} isPdf={true} />;
case 'Style 8':
return <PrintStyle8 {...commonProps} isPdf={true} />;
case 'Style 9':
return <PrintStyle9 {...commonProps} isPdf={true} />;
case 'Style 10':
return <PrintStyle10 {...commonProps} isPdf={true} />;
case 'Style 11':
return <PrintStyle11 {...commonProps} isPdf={true} />;
case 'Style 12':
// return <PrintStyle12 {...commonProps} isPdf={true}/>;
// case 'Style 13':
return <PrintStyle13 {...commonProps} isPdf={true} />;
case 'A4':
return <PrintA4Style11 {...commonProps} isPdf={true} />;
case 'A5':
return <PrintA5Style11 {...commonProps} isPdf={true} />;
case 'A4Standard':
return <InvoiceTemplate {...commonProps} isPdf={true} />;
case 'TaxInvoice':
return <TaxInvoice {...commonProps} isPdf={true} />;
default:
return <PrintStyle1 {...commonProps} isPdf={true} />;
}
};
return (
<div key={index} className="invoice-page">
<div
className="Reprint-body"
id={`RePrint-${index}`}
style={{ width: '50%' }}
/>
{renderPrintTemplate()}
{/* Token Print */}
{/* {Object.entries(tokenGroups).map(
([counterName, items], tokenIdx) => (
<div key={`token-${index}-${tokenIdx}`} className="token-page">
<div
className="Reprint-body"
id={`TakeawayToken${index}-${tokenIdx}-${counterName}`}
style={{ width: '50%' }}
>
<div className="Re-print-top">
<p className="Re-print-title">{items[0]?.BrName}</p>
<p className="Re-print-title">Token</p>
</div>
<div className="Re-print-top2">
<p className="Re-print-bold">
BillNo:&nbsp;
{OrderId && extractLastNumberOrderId(OrderId, FYStatus)}
</p>
<p className="Re-print-bold">{Date1}</p>
</div>
<table className="Re-print-Table">
<thead className="Re-print-th">
<tr>
<th className="Re-print-td">S.No</th>
<th className="Re-print-td">Item</th>
<th className="Re-print-td">Qty</th>
<th className="Re-print-td">Rate</th>
<th className="Re-print-td">Price</th>
</tr>
</thead>
<tbody>
{items?.map((item, idx) => (
<tr className="Re-print-body-Tr" key={idx}>
<td className="Re-print-td">{idx + 1}</td>
<td className="Re-print-body-Product">
{item.ProdName}
</td>
<td className="Re-print-body-Rate">
{item.SalesQty}
</td>
<td className="Re-print-body-Rate">
{item.Rate}
</td>
<td className="Re-print-body-Rate">
{item.TotalAmt}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)
)} */}
</div>
);
})}
</div>
</div>
</>
);
};
export default LinkUrlCreate;