ok
This commit is contained in:
@@ -4,20 +4,26 @@ import { useParams } from "react-router-dom"; // Changed from useSearchParams to
|
||||
import { ThreeDots, ColorRing } from "react-loader-spinner";
|
||||
|
||||
import ItemLister from "../components/ItemLister";
|
||||
|
||||
import { getCartDetails } from "../helpers/itemHelper";
|
||||
|
||||
import { getPaymentMethods } from "../helpers/cafeHelpers.js";
|
||||
|
||||
import {
|
||||
handlePaymentFromClerk,
|
||||
handlePaymentFromGuestSide,
|
||||
handlePaymentFromGuestDevice,
|
||||
handleExtendFromGuestDevice,
|
||||
handleCloseBillFromGuestDevice
|
||||
} from "../helpers/transactionHelpers";
|
||||
|
||||
import { getItemsByCafeId } from "../helpers/cartHelpers.js";
|
||||
|
||||
|
||||
import Dropdown from "./Dropdown.js";
|
||||
import { useNavigationHelpers } from "../helpers/navigationHelpers";
|
||||
|
||||
|
||||
export default function Invoice({ shopId, table, sendParam, deviceType, socket, shopItems, setShopItems }) {
|
||||
export default function Invoice({ shopId, setModal, table, sendParam, deviceType, socket, shopItems, setShopItems }) {
|
||||
const { shopIdentifier, tableCode } = useParams();
|
||||
|
||||
sendParam({ shopIdentifier, tableCode });
|
||||
@@ -37,6 +43,28 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
const [email, setEmail] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const [dropdownKey, setDropdownKey] = useState(0);
|
||||
|
||||
const [paymentMethods, setPaymentMethods] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPaymentMethods = async () => {
|
||||
try {
|
||||
const methods = await getPaymentMethods(shopId);
|
||||
console.log(methods)
|
||||
const lastTransaction = JSON.parse(localStorage.getItem('lastTransaction'));
|
||||
if (lastTransaction?.payment_type == 'paylater') methods.isOpenBillAvailable = false;
|
||||
setPaymentMethods(methods)
|
||||
|
||||
} catch (err) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
if (shopId) {
|
||||
fetchPaymentMethods();
|
||||
}
|
||||
}, [shopId, dropdownKey]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCartItems = async () => {
|
||||
@@ -115,7 +143,7 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
try {
|
||||
// Fetch items from the cart details (latest state)
|
||||
const items = await getCartDetails(shopId);
|
||||
|
||||
|
||||
// Loop through each item type in the items from the cart details
|
||||
items.forEach(itemType => {
|
||||
itemType.itemList.forEach(item => {
|
||||
@@ -132,10 +160,10 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// After updating shopItems, set the new state
|
||||
setShopItems(shopItems);
|
||||
|
||||
|
||||
// Filter out unavailable items
|
||||
const filteredItems = items
|
||||
.map((itemType) => ({
|
||||
@@ -143,13 +171,13 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
itemList: itemType.itemList.filter((item) => item.availability),
|
||||
}))
|
||||
.filter((itemType) => itemType.itemList.length > 0); // Remove empty itemTypes
|
||||
|
||||
|
||||
setIsLoading(true);
|
||||
setTimeout(function () {
|
||||
setCartItems(filteredItems);
|
||||
setIsLoading(false);
|
||||
}, 100); // delay is in milliseconds
|
||||
|
||||
|
||||
// Update local storage by removing unavailable items and updating prices
|
||||
const updatedLocalStorage = JSON.parse(localStorage.getItem("cart")) || [];
|
||||
const newLocalStorage = updatedLocalStorage.map((cafe) => {
|
||||
@@ -161,16 +189,16 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
const updatedItem = filteredItems
|
||||
.flatMap((itemType) => itemType.itemList)
|
||||
.find((filtered) => filtered.itemId === item.itemId);
|
||||
|
||||
|
||||
if (updatedItem) {
|
||||
// Update the price in the local storage item
|
||||
return {
|
||||
...item,
|
||||
price: updatedItem.promoPrice?updatedItem.promoPrice:updatedItem.price,
|
||||
price: updatedItem.promoPrice ? updatedItem.promoPrice : updatedItem.price,
|
||||
availability: updatedItem.availability
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// If no updated item found, return the original item
|
||||
return item;
|
||||
}),
|
||||
@@ -178,7 +206,7 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
}
|
||||
return cafe;
|
||||
});
|
||||
|
||||
|
||||
const newLocalStoragee = newLocalStorage.map((cafe) => {
|
||||
if (cafe.cafeId === shopId) {
|
||||
return {
|
||||
@@ -195,15 +223,15 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
return cafe;
|
||||
});
|
||||
localStorage.setItem("cart", JSON.stringify(newLocalStoragee));
|
||||
|
||||
|
||||
window.dispatchEvent(new Event("localStorageUpdated"));
|
||||
|
||||
|
||||
// Calculate total price based on filtered cart items
|
||||
const totalPrice = filteredItems.reduce((total, itemType) => {
|
||||
return (
|
||||
total +
|
||||
itemType.itemList.reduce((subtotal, item) => {
|
||||
return subtotal + item.qty * (item.promoPrice ? item.promoPrice:item.price);
|
||||
return subtotal + item.qty * (item.promoPrice ? item.promoPrice : item.price);
|
||||
}, 0)
|
||||
);
|
||||
}, 0);
|
||||
@@ -212,42 +240,66 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
console.error("Error fetching cart items:", error);
|
||||
// Handle error if needed
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
getNewestCartItems();
|
||||
}, [shopId]);
|
||||
|
||||
const handlePay = async (isCash) => {
|
||||
const handlePayCloseBill = async (orderMethod) =>{
|
||||
setIsPaymentLoading(true);
|
||||
console.log("tipe" + deviceType);
|
||||
if (deviceType == "clerk") {
|
||||
const pay = await handlePaymentFromClerk(
|
||||
shopId,
|
||||
email,
|
||||
isCash ? "cash" : "cashless",
|
||||
orderType,
|
||||
tableNumber
|
||||
);
|
||||
} else if (deviceType == "guestSide") {
|
||||
const pay = await handlePaymentFromGuestSide(
|
||||
shopId,
|
||||
email,
|
||||
isCash ? "cash" : "cashless",
|
||||
orderType,
|
||||
tableNumber
|
||||
);
|
||||
} else if (deviceType == "guestDevice") {
|
||||
if (transactionData) {
|
||||
const socketId = socket.id;
|
||||
const pay = await handlePaymentFromGuestDevice(
|
||||
const pay = await handleCloseBillFromGuestDevice(
|
||||
transactionData.transactionId,
|
||||
orderMethod,
|
||||
socketId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const handlePay = async (orderMethod) => {
|
||||
setIsPaymentLoading(true);
|
||||
console.log("tipe" + deviceType);
|
||||
if (transactionData) {
|
||||
const socketId = socket.id;
|
||||
const pay = await handleExtendFromGuestDevice(
|
||||
shopId,
|
||||
isCash ? "cash" : "cashless",
|
||||
orderType,
|
||||
table.tableNo || tableNumber,
|
||||
transactionData.transactionId,
|
||||
textareaRef.current.value,
|
||||
socketId
|
||||
);
|
||||
}
|
||||
else
|
||||
|
||||
if (deviceType == "clerk") {
|
||||
const pay = await handlePaymentFromClerk(
|
||||
shopId,
|
||||
email,
|
||||
orderMethod,
|
||||
orderType,
|
||||
tableNumber
|
||||
);
|
||||
} else if (deviceType == "guestSide") {
|
||||
const pay = await handlePaymentFromGuestSide(
|
||||
shopId,
|
||||
email,
|
||||
orderMethod,
|
||||
orderType,
|
||||
tableNumber
|
||||
);
|
||||
} else if (deviceType == "guestDevice") {
|
||||
const socketId = socket.id;
|
||||
const pay = await handlePaymentFromGuestDevice(
|
||||
shopId,
|
||||
orderMethod,
|
||||
orderType,
|
||||
table.tableNo || tableNumber,
|
||||
textareaRef.current.value,
|
||||
socketId
|
||||
);
|
||||
}
|
||||
|
||||
console.log("transaction from " + deviceType + "success");
|
||||
setIsPaymentLoading(false);
|
||||
@@ -278,7 +330,7 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
console.log(localStorage.getItem('cart'))
|
||||
console.log(cartItems)
|
||||
|
||||
if (localStorage.getItem('cart') == "[]") return;
|
||||
if (localStorage.getItem('cart') == null || localStorage.getItem('cart') == '' || localStorage.getItem('cart') == '[]') return;
|
||||
|
||||
// Parse the local storage cart
|
||||
const localStorageCart = JSON.parse(localStorage.getItem('cart'));
|
||||
@@ -322,12 +374,15 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
const handleEmailChange = (event) => {
|
||||
setEmail(event.target.value);
|
||||
};
|
||||
|
||||
const transactionData = JSON.parse(localStorage.getItem('lastTransaction'));
|
||||
|
||||
return (
|
||||
<div className={styles.Invoice} style={{ height: (getItemsByCafeId(shopId).length > 0 ? '' : '100vh'), minHeight: (getItemsByCafeId(shopId).length > 0 ? '100vh' : '') }}>
|
||||
|
||||
<div onClick={goToShop} style={{ marginLeft: '22px', marginTop: '49px', marginRight: '10px', display: 'flex', flexWrap: 'nowrap', alignItems: 'center', fontSize: '25px' }} ><svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 512 512"><path d="M48,256c0,114.87,93.13,208,208,208s208-93.13,208-208S370.87,48,256,48,48,141.13,48,256Zm212.65-91.36a16,16,0,0,1,.09,22.63L208.42,240H342a16,16,0,0,1,0,32H208.42l52.32,52.73A16,16,0,1,1,238,347.27l-79.39-80a16,16,0,0,1,0-22.54l79.39-80A16,16,0,0,1,260.65,164.64Z" /></svg>Keranjang</div>
|
||||
|
||||
{getItemsByCafeId(shopId) < 1 ?
|
||||
{(transactionData == null && getItemsByCafeId(shopId).length < 1) ?
|
||||
<div style={{ height: '75vh', display: 'flex', justifyContent: 'center', flexDirection: 'column', alignContent: 'center', alignItems: 'center' }}>
|
||||
<div style={{ width: '50%' }}>
|
||||
<svg
|
||||
@@ -342,21 +397,22 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
:
|
||||
(isLoading ? <></> :
|
||||
<>
|
||||
<div className={styles.RoundedRectangle}>
|
||||
{cartItems.map((itemType) => (
|
||||
<ItemLister
|
||||
shopId={shopId}
|
||||
forInvoice={true}
|
||||
key={itemType.id}
|
||||
typeName={itemType.typeName}
|
||||
itemList={itemType.itemList}
|
||||
/>
|
||||
))}
|
||||
{getItemsByCafeId(shopId).length > 0 &&
|
||||
<div className={styles.RoundedRectangle}>
|
||||
{cartItems.map((itemType) => (
|
||||
<ItemLister
|
||||
shopId={shopId}
|
||||
forInvoice={true}
|
||||
key={itemType.id}
|
||||
typeName={itemType.typeName}
|
||||
itemList={itemType.itemList}
|
||||
/>
|
||||
))}
|
||||
|
||||
{table.tableNo != null && (
|
||||
<div className={styles.OrderTypeContainer}>
|
||||
<span htmlFor="orderType">Diantar ke {table.tableNo}</span>
|
||||
{/* <select
|
||||
{table.tableNo != null && (
|
||||
<div className={styles.OrderTypeContainer}>
|
||||
<span htmlFor="orderType">Diantar ke {table.tableNo}</span>
|
||||
{/* <select
|
||||
id="orderType"
|
||||
value={orderType}
|
||||
onChange={handleOrderTypeChange}
|
||||
@@ -367,63 +423,149 @@ export default function Invoice({ shopId, table, sendParam, deviceType, socket,
|
||||
<option value="pickup">Pickup</option>
|
||||
{table == null && <option value="serve">Serve</option>}
|
||||
</select> */}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{orderType === "serve" && table.length < 1 && (
|
||||
<div className={styles.OrderTypeContainer}>
|
||||
<span htmlFor="orderType">Serve to:</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Table Number"
|
||||
value={tableNumber}
|
||||
onChange={handleTableNumberChange}
|
||||
className={styles.TableNumberInput}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.NoteContainer}>
|
||||
<span>Catatan :</span>
|
||||
<span></span>
|
||||
</div>
|
||||
)}
|
||||
{orderType === "serve" && table.length < 1 && (
|
||||
<div className={styles.OrderTypeContainer}>
|
||||
<span htmlFor="orderType">Serve to:</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Table Number"
|
||||
value={tableNumber}
|
||||
onChange={handleTableNumberChange}
|
||||
className={styles.TableNumberInput}
|
||||
|
||||
<div className={styles.NoteContainer}>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className={styles.NoteInput}
|
||||
placeholder="Tambahkan catatan..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.NoteContainer}>
|
||||
<span>Catatan :</span>
|
||||
<span></span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className={styles.NoteContainer}>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className={styles.NoteInput}
|
||||
placeholder="Tambahkan catatan..."
|
||||
/>
|
||||
{transactionData &&
|
||||
<div className={styles.RoundedRectangle} style={{ backgroundColor: '#c3c3c3', fontSize: '15px', display: 'flex', justifyContent: 'space-between' }}>
|
||||
{transactionData.payment_type != 'paylater' ?
|
||||
<>
|
||||
<div onClick={() => setModal('transaction_item', { transactionId: transactionData.transactionId })} className={styles.AddedLastTransaction}>
|
||||
Pesanan akan ditambahkan ke transaksi sebelumnya
|
||||
</div>
|
||||
<div className={styles.CancelAddedLastTransaction} onClick={() => { window.location.reload(); localStorage.removeItem('lastTransaction') }}>
|
||||
<svg
|
||||
style={{ width: '40px', height: '40px' }}
|
||||
className={styles['plusNegative2']}
|
||||
clipRule="evenodd"
|
||||
fillRule="evenodd"
|
||||
strokeLinejoin="round"
|
||||
strokeMiterlimit="2"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m12.002 2c5.518 0 9.998 4.48 9.998 9.998 0 5.517-4.48 9.997-9.998 9.997-5.517 0-9.997-4.48-9.997-9.997 0-5.518 4.48-9.998 9.997-9.998zm0 1.5c-4.69 0-8.497 3.808-8.497 8.498s3.807 8.497 8.497 8.497 8.498-3.807 8.498-8.497-3.808-8.498-8.498-8.498zm-.747 7.75h-3.5c-.414 0-.75.336-.75.75s.336.75.75.75h3.5v3.5c0 .414.336.75.75.75s.75-.336.75-.75v-3.5h3.5c.414 0 .75-.336.75-.75s-.336-.75-.75-.75h-3.5v-3.5c0-.414-.336-.75-.75-.75s-.75.336-.75.75z"
|
||||
fillRule="nonzero"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</>
|
||||
:
|
||||
|
||||
<div className={styles.AddedLastTransaction}>
|
||||
<div>
|
||||
Open bill
|
||||
<div onClick={() => setModal('transaction_item', { transactionId: transactionData.transactionId })}>
|
||||
Lihat tagihan
|
||||
</div>
|
||||
</div>
|
||||
{getItemsByCafeId(shopId).length > 0 ?
|
||||
|
||||
<button className={styles.PayButton3} onClick={() => handlePay(orderMethod)}>
|
||||
{isPaymentLoading ? (
|
||||
<ColorRing height="50" width="50" color="white" />
|
||||
) : (
|
||||
<div>
|
||||
{transactionData ?
|
||||
|
||||
<span>Tambahkan</span>
|
||||
:
|
||||
<span>Pesan</span>
|
||||
}
|
||||
|
||||
<span>Rp{totalPrice}</span>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
:
|
||||
|
||||
<button className={styles.PayButton3} style={{ backgroundColor: 'rgb(42 145 24)', letterSpacing: '1px' }} onClick={goToShop}>
|
||||
<div>
|
||||
<span>Tambahkan item lain</span>
|
||||
</div>
|
||||
</button>}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div className={styles.PaymentOption}>
|
||||
<div className={styles.TotalContainer}>
|
||||
<span>Pembayaran</span>
|
||||
<span>
|
||||
<select
|
||||
style={{ borderRadius: '6px', fontSize: '20px' }}
|
||||
id="orderMethod"
|
||||
value={orderMethod}
|
||||
onChange={handleOrderMethodChange}
|
||||
>
|
||||
<option value="cash"> Tunai</option>
|
||||
<option value="cashless"> Non Tunai </option>
|
||||
</select>
|
||||
{paymentMethods != null && <Dropdown setDropdownKey={() => setDropdownKey(dropdownKey + 1)} paymentMethods={paymentMethods} onChange={handleOrderMethodChange} />}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', paddingLeft: '25px', paddingRight: '25px' }}>
|
||||
<button className={styles.PayButton} onClick={() => handlePay(orderMethod == 'cash' ? true : false)}>
|
||||
{isPaymentLoading ? (
|
||||
<ColorRing height="50" width="50" color="white" />
|
||||
) : (
|
||||
<div>
|
||||
<span>Pesan</span>
|
||||
{transactionData && transactionData.payment_type === 'paylater' ?
|
||||
<div style={{ display: 'flex', paddingLeft: '25px', paddingRight: '25px', marginTop: '17px' }}>
|
||||
<button className={styles.PayButton} onClick={() => handlePayCloseBill(orderMethod)}>
|
||||
{isPaymentLoading ? (
|
||||
<ColorRing height="50" width="50" color="white" />
|
||||
) : (
|
||||
<div>
|
||||
<span>Tutup bill</span>
|
||||
|
||||
<span>Rp{totalPrice}</span>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<div onClick={goToShop} style={{ textAlign: 'center', marginBottom: '20px' }}>Kembali</div>
|
||||
<span>Rp{
|
||||
transactionData.DetailedTransactions.reduce((total, transaction) => {
|
||||
return total + (transaction.promoPrice == 0 || transaction.promoPrice == null
|
||||
? transaction.price * transaction.qty
|
||||
: transaction.promoPrice * transaction.qty);
|
||||
}, 0)
|
||||
}</span>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
:
|
||||
|
||||
<div style={{ display: 'flex', paddingLeft: '25px', paddingRight: '25px', marginTop: '17px' }}>
|
||||
<button className={styles.PayButton} onClick={() => handlePay(orderMethod)}>
|
||||
{isPaymentLoading ? (
|
||||
<ColorRing height="50" width="50" color="white" />
|
||||
) : (
|
||||
<div>
|
||||
{transactionData ?
|
||||
|
||||
<span>Tambahkan</span>
|
||||
:
|
||||
<span>Pesan</span>
|
||||
}
|
||||
|
||||
<span>Rp{totalPrice}</span>
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className={styles.PaymentOptionMargin}></div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user