Android_Retail/src/Pages/Tablebooking/MergeBill/MergeBill.jsx

535 lines
24 KiB
React
Raw Normal View History

2026-01-27 18:27:29 +05:30
import React, { useState, useEffect } from "react";
import { Modal, Radio, message, Checkbox, Button, Tag } from "antd";
import { BiGitMerge } from "react-icons/bi";
import { PlusOutlined, DeleteOutlined } from "@ant-design/icons";
import { useDispatch } from "react-redux";
import {
getAllDineInTable,
getSelfBookingOrderId,
postMergeOrders
} from "../../../Features/TableBooking/TableBooking";
import { getSession } from "../../../Services/Others";
import { DropDowns } from "../../../Components/Forms/DropDown";
import "./MergeBill.scss";
const MergeBill = (getDiningTypefun) => {
const dispatch = useDispatch();
const AppId = getSession("AppId");
const CompId = getSession("CompId");
const BranchId = getSession("BranchId");
const [mergeModalVisible, setMergeModalVisible] = useState(false);
const [AllTableData, setAllTableData] = useState([]);
const [isLoading, setIsLoading] = useState(false);
// New state for multiple selections
const [selectedItems, setSelectedItems] = useState([
{
id: 1,
type: 'table',
tableId: null,
chairIds: [],
orderId: null,
orderIds: [], // Fixed: Initialize orderIds array
tableData: null
},
{
id: 2,
type: 'table',
tableId: null,
chairIds: [],
orderId: null,
orderIds: [], // Fixed: Initialize orderIds array
tableData: null
}
]);
useEffect(() => {
const fetchTables = async () => {
try {
let data = { AppId, CompId, BranchId };
let Response = await dispatch(getAllDineInTable(data)).unwrap();
if (Response?.data?.statusCode === 1) {
setAllTableData(Response?.data?.data);
}
} catch (error) {
console.error("Error fetching tables:", error);
message.error("Failed to fetch table data");
}
};
fetchTables();
}, [dispatch, AppId, CompId, BranchId]);
const openModal = () => setMergeModalVisible(true);
const closeModal = () => {
setMergeModalVisible(false);
// Reset all states
setSelectedItems([
{ id: 1, type: 'table', tableId: null, chairIds: [], orderId: null, orderIds: [], tableData: null },
{ id: 2, type: 'table', tableId: null, chairIds: [], orderId: null, orderIds: [], tableData: null }
]);
};
// Filter tables that have at least one occupied chair
const UnchoosenTables = AllTableData?.filter((table) =>
table?.ChairLevelSerDetails?.some((chair) => chair?.SalesStatus === "Y")
);
// Add new selection item
const addSelectionItem = () => {
const newId = Math.max(...selectedItems.map(item => item.id)) + 1;
setSelectedItems([...selectedItems, {
id: newId,
type: 'table',
tableId: null,
chairIds: [],
orderId: null,
orderIds: [], // Fixed: Initialize orderIds array
tableData: null
}]);
};
// Remove selection item
const removeSelectionItem = (id) => {
if (selectedItems.length > 2) {
setSelectedItems(selectedItems.filter(item => item.id !== id));
}
};
// Update selection type (table/chair)
const updateSelectionType = (id, type) => {
setSelectedItems(selectedItems.map(item =>
item.id === id
? { ...item, type, chairIds: [], orderId: null, orderIds: [] } // Fixed: Reset orderIds array
: item
));
};
// Update table selection
const updateTableSelection = (id, tableId) => {
const selectedTableData = AllTableData?.find(table => table.TableId === tableId);
setSelectedItems(selectedItems.map(item => {
if (item.id === id) {
let orderId = null;
let orderIds = []; // Fixed: Initialize orderIds array
if (item.type === 'table' && selectedTableData) {
// For table merge, get first available order ID
orderId = selectedTableData.ChairLevelSerDetails?.find(chair => chair.OrderId)?.OrderId || null;
// For table, orderIds contains just the single order ID
orderIds = orderId ? [orderId] : [];
}
return {
...item,
tableId,
tableData: selectedTableData,
chairIds: [],
orderId,
orderIds // Fixed: Set orderIds array
};
}
return item;
}));
};
// Update chair selection (multiple chairs)
const updateChairSelection = (id, chairIds) => {
setSelectedItems(selectedItems.map(item => {
if (item.id === id) {
let orderIds = [];
if (item.tableData && chairIds.length > 0) {
// Get all order IDs from selected chairs
orderIds = chairIds.map(chairId => {
const chair = item.tableData.ChairLevelSerDetails?.find(c => c.ChairId === chairId);
return chair?.OrderId;
}).filter(Boolean);
// Remove duplicates
orderIds = [...new Set(orderIds)];
}
return {
...item,
chairIds,
orderId: orderIds.length > 0 ? orderIds[0] : null, // Use first order ID for primary reference
orderIds // Fixed: Store all unique order IDs for multi-chair merge
};
}
return item;
}));
};
// Get available tables for a specific selection (excluding already selected ones)
const getAvailableTablesForSelection = (currentId, type) => {
const selectedTableIds = selectedItems
.filter(item => item.id !== currentId && item.tableId)
.map(item => item.tableId);
if (type === 'table') {
return UnchoosenTables?.filter(table => !selectedTableIds.includes(table.TableId));
} else {
return UnchoosenTables; // For chair selection, allow same table
}
};
const handleConfirmedMerge = async () => {
try {
// Validate selections - Fixed: Use orderIds array for validation
const validSelections = selectedItems.filter(item => item.orderIds && item.orderIds.length > 0);
if (validSelections.length < 2) {
message.error('Please select at least 2 tables/chairs to merge');
return;
}
setIsLoading(true);
// Fetch full order details for all selected orders
const fetchOrderDetails = async (orderId) => {
const data = { AppId, CompId, BranchId, orderId };
try {
const response = await dispatch(getSelfBookingOrderId(data)).unwrap();
if (response?.data?.statusCode === 1 && response?.data?.data?.length > 0) {
return response.data.data[0];
}
throw new Error('Order not found');
} catch (err) {
console.error(`Failed to fetch order details for ${orderId}:`, err);
throw err;
}
};
// Collect all unique order IDs - Fixed: Use orderIds array
const allOrderIds = [];
validSelections.forEach(item => {
allOrderIds.push(...item.orderIds);
});
// Remove duplicates
const uniqueOrderIds = [...new Set(allOrderIds)];
// Fetch all order details
const orderDetailsPromises = uniqueOrderIds.map(orderId => fetchOrderDetails(orderId));
const orderDetails = await Promise.all(orderDetailsPromises);
if (orderDetails.some(detail => !detail)) {
message.error('Failed to fetch some order details');
return;
}
// Build merge data
const MergeData = {
CompId,
BranchId,
AppId,
CreatedBy: 0,
MergeRequest: 'A',
DeviceUniqueId: '',
MergeOrderDetails: orderDetails.map(data => ({
OrderId: data.OrderId,
SalesId: data.SalesId || '',
OrderType: data.OrderType || 'S',
CustSuppId: data.CustSuppId || '',
Reference: data.Reference || '',
BookingType: data.BookingType || 0,
ServiceProvider: data.ServiceProvider || 0,
ActiveStatus: data.ActiveStatus || 'Y',
RefundAmount: data.RefundAmount || 0,
AddlInfo: data.AddlInfo || '',
BillAmount: data.BillAmount || 0,
OverallDiscSales: data.OverallDiscSales || 0,
OverallDiscEst: data.OverallDiscEst || 0,
TaxAmount: data.TaxAmount || 0,
VatAmount: data.VatAmount || 0,
NetAmount: data.NetAmount || 0,
GivenAmt: data.GivenAmt || 0,
BalGiven: data.BalGiven || 0,
BookingMedia: data.BookingMedia || '',
PaymentStatus: 'P',
ServiceCategory: data.ServiceCategory || 0,
OtherServiceTicketId: data.OtherServiceTicketId || '',
OtherServiceTicketAmount: data.OtherServiceTicketAmount || 0,
OrderDtlDetails: data.productDetails?.map(item => ({
ProdId: item.ProdId,
Type: item.Type || '',
OrderQty: item.OrderQty || 0,
OrderRate: item.OrderRate || 0,
TotalAmt: item.TotalAmt || 0,
TaxAmt: item.TaxAmt || 0,
RefundQty: item.RefundQty || 0,
InwardDtlId: item.InwardDtlId || '',
TaxId: item.TaxId || '',
ProdNameQty: item.ProdNameQty || '',
OrderStatus: item.OrderStatus || '',
OrderType: 'S',
SinglePc: item.SinglePc || '',
BookingType: item.BookingType || 0,
OfferValue: item.OfferValue || 0,
OfferAmt: item.OfferAmt || 0,
SetCount: item.SetCount || 0,
OfferType: item.OfferType || '',
CounterName: item.CounterName || '',
OfferMode: item.OfferMode || '',
OfferMessage: item.OfferMessage || [{}],
ProductIdentifierDtls: item.ProductIdentifierDtls?.map(prod => ({
SerialNumber: prod.SerialNumber || '',
IMEI1: prod.IMEI1 || '',
IMEI2: prod.IMEI2 || '',
MacId: prod.MacId || ''
})) || [],
PackageDtl: item.PackageDtl?.map(pkg => ({
ProdId: pkg.ProdId || '',
ParcelQty: pkg.ParcelQty || 0,
ParcelCount: pkg.ParcelCount || 0,
Total: pkg.Total || 0,
ParcelType: pkg.ParcelType || '',
ProdVariantName: pkg.ProdVariantName || '',
InwardDtlId: pkg.InwardDtlId || ''
})) || []
})) || [],
SalesTableLinkDetails: data.SalesTableLinkDetails?.map(chair => ({
TableId: chair.TableId,
WaiterId: chair.WaiterId,
ChairId: chair.ChairId,
TipsAmount: chair.TipsAmount || 0
})) || [],
ExtraChargeDetails: data.ExtraChargeDetails?.map(extra => ({
ProdId: extra.ProdId || '',
InwardDtlId: extra.InwardDtlId || '',
OrderQty: extra.OrderQty || 0,
OrderRate: extra.OrderRate || 0,
TotalAmt: extra.TotalAmt || 0,
TaxAmt: extra.TaxAmt || 0,
ExtraChargeId: extra.ExtraChargeId || '',
OrderType: extra.OrderType || 'S',
BookingType: extra.BookingType || 0,
SinglePc: extra.SinglePc || '',
TaxId: extra.TaxId || '',
TaxPercentage: extra.TaxPercentage || 0
})) || [],
Type: data.Type || 0,
ApprovalStatus: 'Y'
})),
PaymentDetail: []
};
console.log('MergeData being sent:', MergeData);
// Call the merge API
const postMerge = await dispatch(postMergeOrders(MergeData)).unwrap();
console.log('Merge API Response:', postMerge);
if (postMerge?.data?.statusCode === 1) {
message.success(`Successfully merged ${uniqueOrderIds.length} orders!`);
await getDiningTypefun.getDiningTypefun();
closeModal();
} else {
message.error(postMerge?.data?.message || 'Merge failed');
}
} catch (err) {
console.error('Merge error:', err);
message.error('Something went wrong during merge.');
} finally {
setIsLoading(false);
}
};
return (
<>
<button
onClick={openModal}
className="Chair-levelbutton-takeaway"
>
<BiGitMerge className="merge-icon" />
Merge Bill
</button>
<Modal
open={mergeModalVisible}
title={
<div className="modal-title">
<BiGitMerge className="modal-title-icon" />
Multi-Table/Chair Merge
</div>
}
onOk={handleConfirmedMerge}
onCancel={closeModal}
width={900}
className="merge-modal"
okText="Merge All Orders"
cancelText="Cancel"
confirmLoading={isLoading}
okButtonProps={{
disabled: selectedItems.filter(item => item.orderIds && item.orderIds.length > 0).length < 2,
className: "merge-ok-button"
}}
cancelButtonProps={{
className: "merge-cancel-button"
}}
>
<div className="multi-merge-content">
{selectedItems.map((item, index) => (
<div key={item.id} className="merge-section">
<div className="section-header">
<h4>Selection {index + 1}</h4>
{selectedItems.length > 2 && (
<Button
type="text"
icon={<DeleteOutlined />}
onClick={() => removeSelectionItem(item.id)}
className="remove-selection-btn"
danger
/>
)}
</div>
{/* Radio for merge type */}
<div className="radio-group-wrapper">
<Radio.Group
value={item.type}
onChange={(e) => updateSelectionType(item.id, e.target.value)}
className="merge-radio-group"
>
<Radio value="table" className="merge-radio">Whole Table</Radio>
<Radio value="chair" className="merge-radio">Multiple Chairs</Radio>
</Radio.Group>
</div>
{/* Table selection */}
<div className="dropdown-wrapper">
<DropDowns
options={getAvailableTablesForSelection(item.id, item.type)?.map((option) => ({
value: option.TableId,
label: option.TableName,
})) || []}
label={<label className="dropdown-label required">Table Name</label>}
optionsNames={{ value: "TypeId", label: "TypeName" }}
className="merge-dropdown"
isOnchanges={!!item.tableId}
onChangeFunction={(selectedValues) => {
updateTableSelection(item.id, selectedValues);
}}
valueData={item.tableId}
/>
</div>
{/* Chair selection if type = chair */}
{item.type === "chair" && item.tableData && (
<div className="dropdown-wrapper">
<label className="dropdown-label required">Select Chairs</label>
<div className="chair-selection-grid">
{item.tableData.ChairLevelSerDetails?.filter(chair => chair.SalesStatus === "Y").map(chair => (
<div key={chair.ChairId} className="chair-checkbox-item">
<Checkbox
checked={item.chairIds.includes(chair.ChairId)}
onChange={(e) => {
const newChairIds = e.target.checked
? [...item.chairIds, chair.ChairId]
: item.chairIds.filter(id => id !== chair.ChairId);
updateChairSelection(item.id, newChairIds);
}}
>
<div className="chair-info">
<span className="chair-name">{chair.ChairName}</span>
<span className="chair-order-id">Order: {chair.OrderId}</span>
</div>
</Checkbox>
</div>
))}
</div>
</div>
)}
{/* Selection summary */}
<div className="selection-summary">
{item.type === 'table' && item.tableData && (
<div className="summary-info">
<span>Table: {item.tableData.TableName}</span>
<span>Order ID: {item.orderId}</span>
</div>
)}
{item.type === 'chair' && item.chairIds.length > 0 && (
<div className="summary-info">
<span>Selected Chairs: {item.chairIds.length}</span>
<div className="order-ids-display">
<span>Order IDs: </span>
{item.orderIds?.map((orderId, idx) => (
<Tag key={idx} color="blue" className="order-id-tag">
{orderId}
</Tag>
))}
</div>
</div>
)}
</div>
</div>
))}
{/* Add more selection button */}
<div className="add-selection-wrapper">
<Button
type="dashed"
icon={<PlusOutlined />}
onClick={addSelectionItem}
className="add-selection-btn"
>
Add More Selection
</Button>
</div>
{/* Final Summary */}
<div className="merge-summary">
<h5 className="summary-title">Merge Summary:</h5>
<div className="summary-content">
{selectedItems.map((item, index) => {
if (!item.orderIds || item.orderIds.length === 0) return null;
return (
<div key={item.id} className="summary-item">
<span className="summary-label">Selection {index + 1}:</span>
<div className="summary-details">
{item.type === 'table' && (
<>
<span>Table: {item.tableData?.TableName}</span>
<div className="order-tags">
{item.orderIds.map((orderId, idx) => (
<Tag key={idx} color="green">Order: {orderId}</Tag>
))}
</div>
</>
)}
{item.type === 'chair' && (
<>
<span>Chairs: {item.chairIds.length} selected from {item.tableData?.TableName}</span>
<div className="order-tags">
{item.orderIds.map((orderId, idx) => (
<Tag key={idx} color="orange">Order: {orderId}</Tag>
))}
</div>
</>
)}
</div>
</div>
);
})}
{selectedItems.filter(item => item.orderIds && item.orderIds.length > 0).length < 2 && (
<div className="warning-message">
Please select at least 2 items with active orders to merge.
</div>
)}
</div>
</div>
</div>
</Modal>
</>
);
};
export default MergeBill;