ok
This commit is contained in:
@@ -43,7 +43,8 @@ function CafePage({
|
||||
loading,
|
||||
queue,
|
||||
cartItemsLength,
|
||||
totalPrice
|
||||
totalPrice,
|
||||
lastTransaction
|
||||
}) {
|
||||
|
||||
|
||||
@@ -64,6 +65,7 @@ function CafePage({
|
||||
const [screenMessage, setScreenMessage] = useState("");
|
||||
|
||||
const [isSpotifyNeedLogin, setNeedSpotifyLogin] = useState(true);
|
||||
const [isExceededDeadline, setIsExceededDeadline] = useState(false);
|
||||
|
||||
const [isEditMode, setIsEditMode] = useState(false);
|
||||
const [filterId, setFilterId] = useState(0);
|
||||
@@ -135,8 +137,10 @@ function CafePage({
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
socket.on("joined-room", (response) => {
|
||||
const { isSpotifyNeedLogin } = response;
|
||||
const { isSpotifyNeedLogin, isExceededDeadline } = response;
|
||||
setNeedSpotifyLogin(isSpotifyNeedLogin);
|
||||
if (isExceededDeadline) setModal('unavailable');
|
||||
setIsExceededDeadline(isExceededDeadline);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -206,7 +210,7 @@ function CafePage({
|
||||
/>
|
||||
) : (
|
||||
welcomePageConfig != null && (
|
||||
<div className="Cafe">
|
||||
<div className="Cafe" style={{ filter: isExceededDeadline ? 'grayscale(1)' : '', pointerEvents: isExceededDeadline ? 'none' : '' }}>
|
||||
<body className="App-header">
|
||||
<Header
|
||||
HeaderText={"Menu"}
|
||||
@@ -296,11 +300,15 @@ function CafePage({
|
||||
))}
|
||||
{!isEditMode && (user.username || cartItemsLength > 0) &&
|
||||
<div style={{ marginTop: '10px', height: '40px', position: 'sticky', bottom: '40px', display: 'flex', justifyContent: 'center', alignItems: 'center', textAlign: 'center' }}>
|
||||
{cartItemsLength > 0 &&
|
||||
{(lastTransaction != null || cartItemsLength > 0) &&
|
||||
<div onClick={goToCart} style={{ backgroundColor: '#73a585', width: user.username ? '55vw' : '70vw', height: '40px', borderRadius: '30px', display: 'flex', justifyContent: 'space-between', padding: '0 20px' }}>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', alignContent: 'center' }}>{cartItemsLength} item</div>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', alignContent: 'center' }}>{lastTransaction != null && '+'}{cartItemsLength} item</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', width: '130px' }}>
|
||||
<span style={{ whiteSpace: 'nowrap' }}>Rp{totalPrice}</span>
|
||||
{((lastTransaction == null || lastTransaction?.payment_type != 'paylater')) ?
|
||||
<span style={{ whiteSpace: 'nowrap' }}>Rp{totalPrice}</span>
|
||||
:
|
||||
<span style={{ whiteSpace: 'nowrap' }}>Open bill</span>
|
||||
}
|
||||
<div style={{ display: 'flex', alignItems: 'center', marginLeft: '5px', width: '20px' }}>
|
||||
<svg viewBox="0 0 34 34" style={{ fill: 'white', marginTop: '4px' }}>
|
||||
<path d="M9.79175 24.75C8.09591 24.75 6.72383 26.1375 6.72383 27.8333C6.72383 29.5292 8.09591 30.9167 9.79175 30.9167C11.4876 30.9167 12.8751 29.5292 12.8751 27.8333C12.8751 26.1375 11.4876 24.75 9.79175 24.75ZM0.541748 0.0833435V3.16668H3.62508L9.17508 14.8679L7.09383 18.645C6.84717 19.0767 6.70842 19.5854 6.70842 20.125C6.70842 21.8208 8.09591 23.2083 9.79175 23.2083H28.2917V20.125H10.4392C10.2234 20.125 10.0538 19.9554 10.0538 19.7396L10.1001 19.5546L11.4876 17.0417H22.973C24.1292 17.0417 25.1467 16.4096 25.6709 15.4538L31.1901 5.44834C31.3134 5.23251 31.3751 4.97043 31.3751 4.70834C31.3751 3.86043 30.6813 3.16668 29.8334 3.16668H7.03217L5.583 0.0833435H0.541748ZM25.2084 24.75C23.5126 24.75 22.1405 26.1375 22.1405 27.8333C22.1405 29.5292 23.5126 30.9167 25.2084 30.9167C26.9042 30.9167 28.2917 29.5292 28.2917 27.8333C28.2917 26.1375 26.9042 24.75 25.2084 24.75Z"></path>
|
||||
@@ -310,7 +318,7 @@ function CafePage({
|
||||
</div>
|
||||
}
|
||||
{user.username &&
|
||||
<div onClick={goToTransactions} style={{ backgroundColor: '#73a585', width: '15vw', height: '40px', borderRadius: '30px', display: 'flex', justifyContent: 'center', marginLeft: cartItemsLength > 0 ? '6px' : '0px' }}>
|
||||
<div onClick={goToTransactions} style={{ backgroundColor: '#73a585', width: '15vw', height: '40px', borderRadius: '30px', display: 'flex', justifyContent: 'center', marginLeft: lastTransaction != null || cartItemsLength > 0 ? '6px' : '0px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '38px', marginRight: '5px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', marginLeft: '5px', width: '20px' }}>
|
||||
<svg viewBox="0 0 512 512">
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -46,7 +46,7 @@ const CreateCouponPage = () => {
|
||||
let encodedCouponCode = encodeURIComponent(encryptedCouponCode);
|
||||
|
||||
// Construct the URL with the encoded coupon code as a query parameter
|
||||
const urlWithCoupon = `https://coupon.kedaimaster.com/coupon?c=${encodedCouponCode}`;
|
||||
const urlWithCoupon = `https://dev.coupon.kedaimaster.com/coupon?c=${encodedCouponCode}`;
|
||||
|
||||
// Optionally, set the URL to use with the coupon
|
||||
setCouponUrl(urlWithCoupon);
|
||||
|
||||
162
src/pages/Dropdown.js
Normal file
162
src/pages/Dropdown.js
Normal file
@@ -0,0 +1,162 @@
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import styles from "./Dropdown.module.css"; // Import the CSS Module
|
||||
|
||||
const icons = {
|
||||
cash: (
|
||||
<svg
|
||||
fill="#000000"
|
||||
viewBox="0 0 96 96"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20px"
|
||||
height="20px"
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g
|
||||
id="SVGRepo_tracerCarrier"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
{" "}
|
||||
<title></title>{" "}
|
||||
<g>
|
||||
{" "}
|
||||
<path d="M90,12H6a5.9966,5.9966,0,0,0-6,6V78a5.9966,5.9966,0,0,0,6,6H90a5.9966,5.9966,0,0,0,6-6V18A5.9966,5.9966,0,0,0,90,12ZM24,72A12.0119,12.0119,0,0,0,12,60V36A12.0119,12.0119,0,0,0,24,24H72A12.0119,12.0119,0,0,0,84,36V60A12.0119,12.0119,0,0,0,72,72Z"></path>{" "}
|
||||
<path d="M48,36A12,12,0,1,0,60,48,12.0119,12.0119,0,0,0,48,36Z"></path>{" "}
|
||||
</g>{" "}
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
cashless: (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlSpace="preserve"
|
||||
width="20px"
|
||||
height="20px"
|
||||
viewBox="0 0 21000 7750"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
{`
|
||||
.fil0 { fill: black; fill-rule: nonzero; }
|
||||
`}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="__x0023_Layer_x0020_1">
|
||||
<metadata id="CorelCorpID_0Corel-Layer" />
|
||||
<path
|
||||
className="fil0"
|
||||
d="M20140 4750l0 -667 0 -1333 -2000 0 -1333 0 0 -667 3333 0 0 -1333 -3333 0 -2000 0 0 1333 0 667 0 1333 2000 0 1333 0 0 667 -3333 0 0 1333 3333 0 2000 0 0 -1333zm527 -417l0 2167c0,44 -18,87 -49,118 -31,31 -74,49 -118,49l-2167 0 0 333 2500 0c44,0 87,-18 118,-49 31,-31 49,-74 49,-118l0 -2500 -333 0zm-18000 -4333l-2500 0c-44,0 -87,18 -118,49 -31,31 -49,74 -49,118l0 2500 333 0 0 -2167c0,-44 18,-87 49,-118 31,-31 74,-49 118,-49l2167 0 0 -333zm2140 7750l1333 0 0 -3000 -1333 0 0 3000zm1167 -7000l-3167 0 0 1333 2000 0 0 2000 1333 0 0 -3167c0,-44 -18,-87 -49,-118 -31,-31 -74,-49 -118,-49zm-3833 0l-1167 0c-44,0 -87,18 -118,49 -31,31 -49,74 -49,118l0 5000c0,44 18,87 49,118 31,31 74,49 118,49l3167 0 0 -1333 -2000 0 0 -4000zm667 3333l1333 0 0 -1333 -1333 0 0 1333zm333 -1000l0 0 667 0 0 667 -667 0 0 -667zm3667 -2333l0 1333 4000 0 0 667 -2667 0 -1333 0 0 1333 0 2000 1333 0 0 -1980 2000 1980 2000 0 -2087 -2000 753 0 1333 0 0 -1333 0 -667 0 -1333 -1333 0 -4000 0zm6000 5333l1333 0 0 -5333 -1333 0 0 5333z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
paylater: (
|
||||
<svg
|
||||
fill="black"
|
||||
height="20px"
|
||||
width="20px"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 409.221 409.221"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
enableBackground="new 0 0 409.221 409.221"
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
|
||||
<g
|
||||
id="SVGRepo_tracerCarrier"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<path d="m115.921,208.425c0-5.523 4.477-10 10-10h56.421c5.523,0 10,4.477 10,10s-4.477,10-10,10h-56.421c-5.523,0-10-4.477-10-10zm10,54.838h56.421c5.523,0 10-4.477 10-10s-4.477-10-10-10h-56.421c-5.523,0-10,4.477-10,10s4.477,10 10,10zm32.2-159.536c0-19.248 20.421-34.326 46.49-34.326 26.068,0 46.488,15.078 46.488,34.326s-20.42,34.326-46.488,34.326c-26.069,0-46.49-15.078-46.49-34.326zm20,0c0,6.762 11.329,14.326 26.49,14.326 15.16,0 26.488-7.563 26.488-14.326s-11.328-14.326-26.488-14.326c-15.162-1.42109e-14-26.49,7.564-26.49,14.326zm-52.2,81.176h56.421c5.523,0 10-4.477 10-10s-4.477-10-10-10h-56.421c-5.523,0-10,4.477-10,10s4.477,10 10,10zm157.381,155.665h-32.818c-5.522,0-10,4.477-10,10s4.478,10 10,10h32.818c5.522,0 10-4.477 10-10s-4.478-10-10-10zm59.627-330.568v389.221c0,5.523-4.478,10-10,10h-256.637c-5.523,0-10-4.477-10-10v-389.221c0-5.523 4.477-10 10-10h36.662c5.523,0 10,4.477 10,10v14.327h16.662v-14.327c0-5.523 4.477-10 10-10h36.663c5.523,0 10,4.477 10,10v14.327h16.663v-14.327c0-5.523 4.478-10 10-10h36.662c5.522,0 10,4.477 10,10v14.327h16.663v-14.327c0-5.523 4.478-10 10-10h36.661c5.523,0 10.001,4.477 10.001,10zm-20,10h-16.661v14.327c0,5.523-4.478,10-10,10h-36.663c-5.522,0-10-4.477-10-10v-14.327h-16.662v14.327c0,5.523-4.478,10-10,10h-36.663c-5.523,0-10-4.477-10-10v-14.327h-16.663v14.327c0,5.523-4.477,10-10,10h-36.662c-5.523,0-10-4.477-10-10v-14.327h-16.663v369.221h236.637v-369.221zm-39.627,178.425h-32.818c-5.522,0-10,4.477-10,10s4.478,10 10,10h32.818c5.522,0 10-4.477 10-10s-4.478-10-10-10zm0,44.838h-32.818c-5.522,0-10,4.477-10,10s4.478,10 10,10h32.818c5.522,0 10-4.477 10-10s-4.478-10-10-10zm0,48.653h-157.381c-5.523,0-10,4.477-10,10s4.477,10 10,10h157.381c5.522,0 10-4.477 10-10s-4.478-10-10-10z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
),
|
||||
};
|
||||
|
||||
const Dropdown = ({ onChange, paymentMethods, setDropdownKey }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedOption, setSelectedOption] = useState("cash");
|
||||
const dropdownRef = useRef(null);
|
||||
|
||||
const toggleDropdown = () => {
|
||||
setDropdownKey();
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const handleClickOutside = (event) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, []);
|
||||
|
||||
const handleOptionClick = (value) => {
|
||||
setSelectedOption(value);
|
||||
setIsOpen(false);
|
||||
|
||||
if (onChange) onChange({ target: { value } });
|
||||
};
|
||||
|
||||
const options = [
|
||||
{ value: "cash", label: "Tunai", icon: icons.cash },
|
||||
{ value: "cashless", label: "Non tunai", icon: icons.cashless },
|
||||
{ value: "paylater", label: "Open bill", icon: icons.paylater },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.dropdown} ref={dropdownRef}>
|
||||
<div className={styles.dropdownButton} onClick={toggleDropdown}>
|
||||
<a>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div className={styles.svg}>
|
||||
{icons[selectedOption]}
|
||||
</div>
|
||||
{options.find((option) => option.value === selectedOption)?.label}
|
||||
</div>
|
||||
<span
|
||||
className={styles.arrow}
|
||||
style={{
|
||||
color: 'black'
|
||||
}}
|
||||
>
|
||||
{isOpen ? "▲" : "▼"}
|
||||
</span>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div className={styles.dropdownContent}>
|
||||
{options
|
||||
.filter((option) => {
|
||||
// Filter out 'cashless' option if QRIS is not available
|
||||
if (option.value === 'cashless' && !paymentMethods.isQRISavailable) {
|
||||
return false;
|
||||
}
|
||||
// Filter out 'paylater' option if Open Bill is not available
|
||||
if (option.value === 'paylater' && !paymentMethods.isOpenBillAvailable) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map((option) => (
|
||||
<a onClick={() => handleOptionClick(option.value)} key={option.value}>
|
||||
<div className={styles.svg}>
|
||||
{option.icon}
|
||||
</div>
|
||||
{option.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dropdown;
|
||||
60
src/pages/Dropdown.module.css
Normal file
60
src/pages/Dropdown.module.css
Normal file
@@ -0,0 +1,60 @@
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdownButton {
|
||||
background-color: #fff;
|
||||
width: 150px;
|
||||
color: #000;
|
||||
border: 1px solid #000;
|
||||
cursor: pointer;
|
||||
font-size: 17px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.dropdownButton a {
|
||||
color: #000;
|
||||
padding: 8px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
transition: background-color 0.3s ease;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
line-height: 19px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.dropdownButton i {
|
||||
margin-left: 8px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.dropdownContent {
|
||||
position: absolute;
|
||||
bottom: 105%;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
width: 150px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
border: 1px solid #000;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.dropdownContent a {
|
||||
color: #000;
|
||||
padding: 8px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
transition: background-color 0.3s ease;
|
||||
border-bottom: 1px solid #ddd;
|
||||
display: flex;
|
||||
line-height: 19px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.svg {
|
||||
width: 27px;
|
||||
height: 20px;
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
}
|
||||
|
||||
.PaymentOption {
|
||||
overflow-x: hidden;
|
||||
overflow: visible;
|
||||
background-color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -72,7 +72,7 @@
|
||||
font-size: 1.3em;
|
||||
padding: 10px 0;
|
||||
padding-top: 20px;
|
||||
margin-bottom: 17px;
|
||||
/* margin-bottom: 17px; */
|
||||
}
|
||||
|
||||
.PayButton {
|
||||
@@ -98,7 +98,30 @@
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.PayButton3{
|
||||
font-family: "Plus Jakarta Sans", sans-serif;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-size: 20px;
|
||||
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
border-radius: 50px;
|
||||
background-color: rgba(88, 55, 50, 1);
|
||||
color: white;
|
||||
border: none;
|
||||
margin: 0px auto;
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
.PayButton3 div{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
.Pay2Button {
|
||||
text-align: center;
|
||||
color: rgba(88, 55, 50, 1);
|
||||
@@ -178,3 +201,23 @@
|
||||
resize: none; /* Prevent resizing */
|
||||
overflow-wrap: break-word; /* Ensure text wraps */
|
||||
}
|
||||
|
||||
|
||||
.AddedLastTransaction{
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
padding: 10px 20px;
|
||||
margin-bottom: 7px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.AddedLastTransaction div{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.CancelAddedLastTransaction{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 30px;
|
||||
margin-top: 10px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
@@ -121,8 +121,8 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
|
||||
<ul>
|
||||
{transaction.DetailedTransactions.map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty} x Rp{" "}
|
||||
{detail.promoPrice ? detail.promoPrice : detail.price}
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -144,6 +144,10 @@ const App = ({ forCafe = true, cafeId = -1,
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCouponList(coupons)
|
||||
}, [coupons]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedCafeId(cafeId)
|
||||
}, [cafeId]);
|
||||
|
||||
@@ -20,8 +20,11 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
const [isPaymentLoading, setIsPaymentLoading] = useState(false);
|
||||
const [searchParams] = useSearchParams();
|
||||
const [transaction, setTransaction] = useState(null);
|
||||
const [notAcceptedItems, setNotAcceptedItems] = useState([]);
|
||||
const noteRef = useRef(null);
|
||||
|
||||
const [transactionRefreshKey, setTransactionRefreshKey] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const transactionId = searchParams.get("transactionId") || "";
|
||||
|
||||
@@ -60,9 +63,11 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
if (isPaymentLoading) return;
|
||||
setIsPaymentLoading(true);
|
||||
try {
|
||||
const c = await confirmTransaction(transactionId);
|
||||
const c = await confirmTransaction(transactionId, notAcceptedItems);
|
||||
if (c) {
|
||||
setTransaction({ ...transaction, confirmed: c.confirmed });
|
||||
console.log(c)
|
||||
setTransaction(c);
|
||||
setTransactionRefreshKey(transactionRefreshKey+1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error processing payment:", error);
|
||||
@@ -76,6 +81,11 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
setIsPaymentLoading(true);
|
||||
try {
|
||||
const c = await declineTransaction(transactionId);
|
||||
if (c) {
|
||||
console.log(c)
|
||||
setTransaction({ ...transaction, confirmed: c.confirmed });
|
||||
setTransactionRefreshKey(transactionRefreshKey+1);
|
||||
}
|
||||
// if (c) {
|
||||
// // Update the confirmed status locally
|
||||
// setTransactions((prevTransactions) =>
|
||||
@@ -107,7 +117,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
}, [transaction?.notes]);
|
||||
|
||||
return (
|
||||
<div className={styles.Transaction}>
|
||||
<div key={transactionRefreshKey} className={styles.Transaction}>
|
||||
|
||||
<div className={styles.TransactionListContainer}>
|
||||
{transaction && (
|
||||
@@ -120,15 +130,17 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
>
|
||||
|
||||
<div className={styles['receipt-header']}>
|
||||
<img
|
||||
src={shopImg} // Placeholder image URL
|
||||
alt='logo'
|
||||
className={styles['receipt-logo']}
|
||||
/>
|
||||
<div className={styles['receipt-info']}>
|
||||
<h3>Receipt Information</h3>
|
||||
<h3>{transaction.payment_type == 'cash' ? 'Tunai' : transaction.payment_type == 'cashless' ? 'Non tunai' : transaction.payment_type == 'paylater' ? 'Open bill' :'Close bill' }</h3>
|
||||
<p>Transaction ID: {transaction.transactionId}</p>
|
||||
<p>Payment Type: {transaction.payment_type}</p>
|
||||
{
|
||||
transaction.payment_type == 'paylater/cash' ?
|
||||
<p>Cash</p>
|
||||
:
|
||||
(transaction.payment_type == 'paylater/cashless' &&
|
||||
<p>Non tunai</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -147,9 +159,9 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
<path d="M 0.198 14.001 C -0.192 14.392 -0.088 11.275 0.1 11.088 L 7.293 4.293 C 7.684 3.902 8.316 3.902 8.707 4.293 L 15.833 11.258 C 16.021 11.445 16.203 14.644 15.812 14.253 L 8 6.414 L 0.198 14.001 Z" fill="#dbdbdb"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div className={styles.circle}>1</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.circle}>1</div>
|
||||
</div>
|
||||
<div className={styles['line']} ></div>
|
||||
<div className={styles['circle-right']} onClick={() => handleMoveToTransaction('next', transaction.transactionId)}>
|
||||
<div className={styles["inner-circle"]}>
|
||||
@@ -171,14 +183,52 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.RibbonBanner}></div>
|
||||
<ul>
|
||||
<div>
|
||||
{transaction.DetailedTransactions.map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty} x Rp{" "}
|
||||
{detail.promoPrice ? detail.promoPrice : detail.price}
|
||||
</li>
|
||||
<div className={styles['itemContainer']} key={detail.detailedTransactionId}>
|
||||
<div className={styles['plusNegative']} style={{ visibility: transaction.confirmed >= 0 && detail.acceptedStatus == 0 ? 'visible' : 'hidden', margin: transaction.confirmed >= 0 && detail.acceptedStatus == 0 ? '4px 10px 0px 10px' : '4px 0px 0px 0px' }}
|
||||
onClick={() =>
|
||||
setNotAcceptedItems(prevState =>
|
||||
prevState.includes(detail.detailedTransactionId)
|
||||
? prevState.filter(item => item !== detail.detailedTransactionId)
|
||||
: [...prevState, detail.detailedTransactionId]
|
||||
)
|
||||
}
|
||||
>
|
||||
{!notAcceptedItems.includes(detail.detailedTransactionId) ?
|
||||
<svg
|
||||
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>
|
||||
:
|
||||
<svg className={styles['plusNegative2']}
|
||||
viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M25 38c-5.1 0-9.7-3-11.8-7.6l1.8-.8c1.8 3.9 5.7 6.4 10 6.4 6.1 0 11-4.9 11-11s-4.9-11-11-11c-4.6 0-8.5 2.8-10.1 7.3l-1.9-.7c1.9-5.2 6.6-8.6 12-8.6 7.2 0 13 5.8 13 13s-5.8 13-13 13z" /><path d="M20 22h-8v-8h2v6h6z" /></svg>
|
||||
}
|
||||
</div>
|
||||
|
||||
<span style={{
|
||||
display: 'inline-block',
|
||||
maxWidth: '100px', // Adjust the max-width as needed
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis'
|
||||
}}>
|
||||
{detail.Item.name}</span> - {notAcceptedItems.includes(detail.detailedTransactionId) || detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
|
||||
</div>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
{transaction.serving_type === "pickup"
|
||||
? "Ambil sendiri"
|
||||
|
||||
@@ -37,6 +37,7 @@ export default function Transactions({
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTransaction = await getTransaction(transactionId);
|
||||
if (fetchedTransaction.payment_type == 'paylater' && deviceType == 'guestDevice') localStorage.setItem('lastTransaction', JSON.stringify(fetchedTransaction));
|
||||
setTransaction(fetchedTransaction);
|
||||
console.log(fetchedTransaction); // Log the fetched transaction
|
||||
} catch (error) {
|
||||
@@ -135,9 +136,36 @@ export default function Transactions({
|
||||
>
|
||||
|
||||
<div className={styles['receipt-header']}>
|
||||
<ColorRing className={styles['receipt-logo']} />
|
||||
{transaction.payment_type == 'paylater' ?
|
||||
<div>
|
||||
<svg
|
||||
height="60px"
|
||||
width="60px"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 506.4 506.4"
|
||||
xmlSpace="preserve"
|
||||
fill="#000000"
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
|
||||
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<circle style={{ fill: '#54B265' }} cx="253.2" cy="253.2" r="249.2" />
|
||||
<path
|
||||
style={{ fill: '#F4EFEF' }}
|
||||
d="M372.8,200.4l-11.2-11.2c-4.4-4.4-12-4.4-16.4,0L232,302.4l-69.6-69.6c-4.4-4.4-12-4.4-16.4,0 L134.4,244c-4.4,4.4-4.4,12,0,16.4l89.2,89.2c4.4,4.4,12,4.4,16.4,0l0,0l0,0l10.4-10.4l0.8-0.8l121.6-121.6 C377.2,212.4,377.2,205.2,372.8,200.4z"
|
||||
></path>
|
||||
<path d="M253.2,506.4C113.6,506.4,0,392.8,0,253.2S113.6,0,253.2,0s253.2,113.6,253.2,253.2S392.8,506.4,253.2,506.4z M253.2,8 C118,8,8,118,8,253.2s110,245.2,245.2,245.2s245.2-110,245.2-245.2S388.4,8,253.2,8z"></path>
|
||||
<path d="M231.6,357.2c-4,0-8-1.6-11.2-4.4l-89.2-89.2c-6-6-6-16,0-22l11.6-11.6c6-6,16.4-6,22,0l66.8,66.8L342,186.4 c2.8-2.8,6.8-4.4,11.2-4.4c4,0,8,1.6,11.2,4.4l11.2,11.2l0,0c6,6,6,16,0,22L242.8,352.4C239.6,355.6,235.6,357.2,231.6,357.2z M154,233.6c-2,0-4,0.8-5.6,2.4l-11.6,11.6c-2.8,2.8-2.8,8,0,10.8l89.2,89.2c2.8,2.8,8,2.8,10.8,0l132.8-132.8c2.8-2.8,2.8-8,0-10.8 l-11.2-11.2c-2.8-2.8-8-2.8-10.8,0L234.4,306c-1.6,1.6-4,1.6-5.6,0l-69.6-69.6C158,234.4,156,233.6,154,233.6z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</div> :
|
||||
<ColorRing className={styles['receipt-logo']} />
|
||||
}
|
||||
<div className={styles['receipt-info']}>
|
||||
{transaction.payment_type == 'cashless' ? <h3>silahkan bayar dengan QRIS</h3> : <h3>silahkan bayar ke kasir</h3>}
|
||||
{transaction.payment_type == 'cashless' || transaction.payment_type == 'paylater/cashless' ? <h3>silahkan bayar dengan QRIS</h3> : transaction.payment_type == 'cash' || transaction.payment_type == 'paylater/cash' ? <h3>silahkan bayar ke kasir</h3> : transaction.DetailedTransactions.some(transaction => transaction.additionalNumber > 0) ? <h3>Item sukses ditambahkan</h3> : <h3>open bill disetujui</h3>}
|
||||
<p>Transaction ID: {transaction.transactionId}</p>
|
||||
<p>Payment Type: {transaction.payment_type}</p>
|
||||
</div>
|
||||
@@ -151,30 +179,32 @@ export default function Transactions({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
Transaction ID: {transaction.transactionId}
|
||||
</h2>
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
Payment Type: {transaction.payment_type}
|
||||
</h2>
|
||||
<ul>
|
||||
{(isPaymentOpen
|
||||
? transaction.DetailedTransactions.slice(0, 2)
|
||||
: transaction.DetailedTransactions
|
||||
).map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty} x Rp{" "}
|
||||
{detail.promoPrice ? detail.promoPrice : detail.price}
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{transaction.payment_type != 'paylater/cash' && transaction.payment_type != 'paylater/cashless' &&
|
||||
<div
|
||||
onClick={() => {
|
||||
localStorage.setItem('lastTransaction', JSON.stringify(transaction));
|
||||
}} className={styles["addNewItem"]}>Tambah pesanan</div>
|
||||
}
|
||||
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
{transaction.serving_type === "pickup"
|
||||
? "Self pickup"
|
||||
: `Serve to ${transaction.Table ? transaction.Table.tableNo : "N/A"
|
||||
? "Ambil sendiri"
|
||||
: `Diantar ke ${transaction.Table ? transaction.Table.tableNo : "N/A"
|
||||
}`}
|
||||
</h2>
|
||||
|
||||
{(transaction.notes !== "") && (
|
||||
<>
|
||||
<div
|
||||
@@ -202,47 +232,86 @@ export default function Transactions({
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className={styles.TotalContainer}>
|
||||
<span>Total:</span>
|
||||
<span>
|
||||
Rp {calculateTotalPrice(transaction.DetailedTransactions)}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.PaymentContainer}>
|
||||
<ButtonWithReplica
|
||||
paymentUrl={paymentUrl}
|
||||
price={
|
||||
"Rp" + calculateTotalPrice(transaction.DetailedTransactions)
|
||||
}
|
||||
disabled={transaction.payment_type == 'cash' || isPaymentLoading}
|
||||
isPaymentLoading={isPaymentLoading}
|
||||
handleClick={() => handleConfirm(transaction.transactionId)}
|
||||
Open={() => setIsPaymentOpen(true)}
|
||||
isPaymentOpen={isPaymentOpen}
|
||||
|
||||
{transaction.payment_type != 'paylater' ?
|
||||
<>
|
||||
<div className={styles.TotalContainer}>
|
||||
<span>Total:</span>
|
||||
<span>
|
||||
Rp {calculateTotalPrice(transaction.DetailedTransactions)}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.PaymentContainer}>
|
||||
{transaction.payment_type != 'cash' && transaction.payment_type != 'paylater/cash' &&
|
||||
<ButtonWithReplica
|
||||
paymentUrl={paymentUrl}
|
||||
price={
|
||||
"Rp" + calculateTotalPrice(transaction.DetailedTransactions)
|
||||
}
|
||||
disabled={isPaymentLoading}
|
||||
isPaymentLoading={isPaymentLoading}
|
||||
handleClick={() => handleConfirm(transaction.transactionId)}
|
||||
Open={() => setIsPaymentOpen(true)}
|
||||
isPaymentOpen={isPaymentOpen}
|
||||
>
|
||||
{
|
||||
isPaymentLoading ? null : isPaymentOpen ? (
|
||||
transaction.paymentClaimed ? (
|
||||
<>
|
||||
<div style={{ position: 'absolute', left: '15px', top: '9px' }}>
|
||||
<ColorRing height="20" width="20" className={styles['receipt-logo']} />
|
||||
</div> Menunggu konfirmasi
|
||||
</>
|
||||
) : 'Klaim telah bayar'
|
||||
) : 'Tampilkan QR'
|
||||
}
|
||||
|
||||
</ButtonWithReplica>
|
||||
}
|
||||
</div>
|
||||
{transaction.payment_type == 'cash' ?
|
||||
<button
|
||||
className={styles.PayButton}
|
||||
onClick={() => handleDecline(transaction.transactionId)}
|
||||
disabled={
|
||||
transaction.confirmed === -1 ||
|
||||
transaction.confirmed === 3 ||
|
||||
isPaymentLoading
|
||||
} // Disable button if confirmed (1) or declined (-1) or
|
||||
>
|
||||
{isPaymentLoading ? (
|
||||
<ColorRing height="50" width="50" color="white" />
|
||||
) : transaction.confirmed === -1 ? (
|
||||
"Ditolak" // Display "Declined" if the transaction is declined (-1)
|
||||
) : transaction.confirmed === -2 ? (
|
||||
"Dibatalkan" // Display "Declined" if the transaction is declined (-1)
|
||||
) : (
|
||||
"Batalkan" // Display "Confirm availability" if the transaction is not confirmed (0)
|
||||
)}
|
||||
</button>
|
||||
:
|
||||
(transaction.payment_type != 'paylater/cash' && transaction.payment_type != 'paylater/cashless' || isPaymentOpen &&
|
||||
<h5
|
||||
className={`${styles.DeclineButton}`}
|
||||
onClick={() =>
|
||||
isPaymentOpen
|
||||
? setIsPaymentOpen(false)
|
||||
: handleDecline(transaction.transactionId)
|
||||
}
|
||||
>
|
||||
{isPaymentOpen ? "kembali" : "batalkan"}
|
||||
</h5>
|
||||
)
|
||||
}
|
||||
</>
|
||||
:
|
||||
|
||||
<h5
|
||||
className={`${styles.DeclineButton}`}
|
||||
>
|
||||
{transaction.payment_type == 'cash' || isPaymentLoading ? (
|
||||
(transaction.payment_type == 'cash' && 'Bayar nanti')
|
||||
) : isPaymentOpen ? (
|
||||
(transaction.paymentClaimed ? <>
|
||||
<div style={{position: 'absolute', left: '15px', top: '9px'}}>
|
||||
<ColorRing
|
||||
height="20"
|
||||
width="20" className={styles['receipt-logo']} /></div> Menunggu konfirmasi</> : "Klaim telah bayar") // Display "Confirm has paid" if the transaction is confirmed (1)
|
||||
) : (
|
||||
"Tampilkan QR" // Display "Confirm availability" if the transaction is not confirmed (0)
|
||||
)}
|
||||
</ButtonWithReplica>
|
||||
</div>
|
||||
<h5
|
||||
className={`${styles.DeclineButton}`}
|
||||
onClick={() =>
|
||||
isPaymentOpen
|
||||
? setIsPaymentOpen(false)
|
||||
: handleDecline(transaction.transactionId)
|
||||
}
|
||||
>
|
||||
{isPaymentOpen ? "kembali" : "batalkan"}
|
||||
</h5>
|
||||
Open bill disetujui, silahkan lanjutkan pemesanan anda
|
||||
</h5>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,240 @@
|
||||
import React from "react";
|
||||
import { ColorRing } from "react-loader-spinner";
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import styles from "./Transactions.module.css";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ColorRing } from "react-loader-spinner";
|
||||
import ButtonWithReplica from "../components/ButtonWithReplica";
|
||||
import {
|
||||
getTransaction,
|
||||
confirmTransaction,
|
||||
handleClaimHasPaid,
|
||||
declineTransaction,
|
||||
cancelTransaction,
|
||||
} from "../helpers/transactionHelpers";
|
||||
import { getTables } from "../helpers/tableHelper";
|
||||
import TableCanvas from "../components/TableCanvas";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
export default function Transaction_pending() {
|
||||
const containerStyle = {
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
height: "100%", // This makes the container stretch to the bottom of the viewport
|
||||
backgroundColor: "#000", // Optional: Set a background color if you want to see the color ring clearly
|
||||
export default function Transactions({
|
||||
propsShopId,
|
||||
sendParam,
|
||||
deviceType,
|
||||
paymentUrl,
|
||||
}) {
|
||||
const { shopId, tableId } = useParams();
|
||||
if (sendParam) sendParam({ shopId, tableId });
|
||||
|
||||
const [tables, setTables] = useState([]);
|
||||
const [selectedTable, setSelectedTable] = useState(null);
|
||||
const [isPaymentLoading, setIsPaymentLoading] = useState(false);
|
||||
const [searchParams] = useSearchParams();
|
||||
const [transaction, setTransaction] = useState(null);
|
||||
const noteRef = useRef(null);
|
||||
const [isPaymentOpen, setIsPaymentOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const transactionId = searchParams.get("transactionId") || "";
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTransaction = await getTransaction(transactionId);
|
||||
if (fetchedTransaction.payment_type == 'paylater' && deviceType == 'guestDevice') localStorage.setItem('lastTransaction', JSON.stringify(fetchedTransaction));
|
||||
setTransaction(fetchedTransaction);
|
||||
console.log(fetchedTransaction); // Log the fetched transaction
|
||||
} catch (error) {
|
||||
console.error("Error fetching transaction:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const waitForLocalStorage = async () => {
|
||||
while (localStorage.getItem("auth") === null) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second
|
||||
}
|
||||
};
|
||||
|
||||
const initialize = async () => {
|
||||
await waitForLocalStorage();
|
||||
await fetchData();
|
||||
};
|
||||
|
||||
initialize();
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTables = await getTables(shopId || propsShopId);
|
||||
setTables(fetchedTables);
|
||||
} catch (error) {
|
||||
console.error("Error fetching tables:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [shopId || propsShopId]);
|
||||
|
||||
const calculateTotalPrice = (detailedTransactions) => {
|
||||
return detailedTransactions.reduce((total, dt) => {
|
||||
return total + dt.qty * (dt.promoPrice ? dt.promoPrice : dt.price);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const handleConfirm = async (transactionId) => {
|
||||
if (isPaymentLoading) return;
|
||||
setIsPaymentLoading(true);
|
||||
try {
|
||||
const c = await handleClaimHasPaid(transactionId);
|
||||
if (c) {
|
||||
setTransaction({
|
||||
...transaction,
|
||||
confirmed: c.confirmed,
|
||||
paymentClaimed: true,
|
||||
});
|
||||
console.log(transaction);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error processing payment:", error);
|
||||
} finally {
|
||||
setIsPaymentLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDecline = async (transactionId) => {
|
||||
if (isPaymentLoading) return;
|
||||
setIsPaymentLoading(true);
|
||||
try {
|
||||
const c = await cancelTransaction(transactionId);
|
||||
} catch (error) {
|
||||
console.error("Error processing payment:", error);
|
||||
} finally {
|
||||
setIsPaymentLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const autoResizeTextArea = (textarea) => {
|
||||
if (textarea) {
|
||||
textarea.style.height = "auto"; // Reset height
|
||||
textarea.style.height = `${textarea.scrollHeight}px`; // Set new height
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (noteRef.current) {
|
||||
autoResizeTextArea(noteRef.current);
|
||||
}
|
||||
}, [transaction?.notes]);
|
||||
|
||||
return (
|
||||
<div className={styles.Transaction}>
|
||||
<div className={containerStyle}>
|
||||
<div style={{ marginTop: "30px", textAlign: "center" }}>
|
||||
<h2>pesananmu selesai diproses nih</h2>
|
||||
<img
|
||||
className={styles.expression}
|
||||
src="https://i.imgur.com/nTokiWH.png"
|
||||
alt="Success"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.TransactionListContainer} style={{ overflow: 'hidden' }}>
|
||||
{transaction && (
|
||||
<div
|
||||
key={transaction.transactionId}
|
||||
className={styles.RoundedRectangle}
|
||||
onClick={() =>
|
||||
setSelectedTable(transaction.Table || { tableId: 0 })
|
||||
}
|
||||
>
|
||||
|
||||
<div className={styles['receipt-header']}>
|
||||
<div>
|
||||
<svg
|
||||
height="60px"
|
||||
width="60px"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 506.4 506.4"
|
||||
xmlSpace="preserve"
|
||||
fill="#000000"
|
||||
style={{marginTop: '12px', marginBottom: '12px'}}
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
|
||||
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<circle style={{ fill: '#54B265' }} cx="253.2" cy="253.2" r="249.2" />
|
||||
<path
|
||||
style={{ fill: '#F4EFEF' }}
|
||||
d="M372.8,200.4l-11.2-11.2c-4.4-4.4-12-4.4-16.4,0L232,302.4l-69.6-69.6c-4.4-4.4-12-4.4-16.4,0 L134.4,244c-4.4,4.4-4.4,12,0,16.4l89.2,89.2c4.4,4.4,12,4.4,16.4,0l0,0l0,0l10.4-10.4l0.8-0.8l121.6-121.6 C377.2,212.4,377.2,205.2,372.8,200.4z"
|
||||
></path>
|
||||
<path d="M253.2,506.4C113.6,506.4,0,392.8,0,253.2S113.6,0,253.2,0s253.2,113.6,253.2,253.2S392.8,506.4,253.2,506.4z M253.2,8 C118,8,8,118,8,253.2s110,245.2,245.2,245.2s245.2-110,245.2-245.2S388.4,8,253.2,8z"></path>
|
||||
<path d="M231.6,357.2c-4,0-8-1.6-11.2-4.4l-89.2-89.2c-6-6-6-16,0-22l11.6-11.6c6-6,16.4-6,22,0l66.8,66.8L342,186.4 c2.8-2.8,6.8-4.4,11.2-4.4c4,0,8,1.6,11.2,4.4l11.2,11.2l0,0c6,6,6,16,0,22L242.8,352.4C239.6,355.6,235.6,357.2,231.6,357.2z M154,233.6c-2,0-4,0.8-5.6,2.4l-11.6,11.6c-2.8,2.8-2.8,8,0,10.8l89.2,89.2c2.8,2.8,8,2.8,10.8,0l132.8-132.8c2.8-2.8,2.8-8,0-10.8 l-11.2-11.2c-2.8-2.8-8-2.8-10.8,0L234.4,306c-1.6,1.6-4,1.6-5.6,0l-69.6-69.6C158,234.4,156,233.6,154,233.6z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className={styles['receipt-info']}>
|
||||
<h3>Transaksi Selesai</h3>
|
||||
<p>Transaction ID: {transaction.transactionId}</p>
|
||||
<p>Payment Type: {transaction.payment_type}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{(isPaymentOpen
|
||||
? transaction.DetailedTransactions.slice(0, 2)
|
||||
: transaction.DetailedTransactions
|
||||
).map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
{transaction.serving_type === "pickup"
|
||||
? "Ambil sendiri"
|
||||
: `Diantar ke ${transaction.Table ? transaction.Table.tableNo : "N/A"
|
||||
}`}
|
||||
</h2>
|
||||
|
||||
{(transaction.notes !== "") && (
|
||||
<>
|
||||
<div
|
||||
className={styles.NoteContainer}
|
||||
style={{
|
||||
visibility: transaction.notes == "" ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<span>Note :</span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={styles.NoteContainer}
|
||||
style={{
|
||||
visibility: transaction.notes == "" ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<textarea
|
||||
className={styles.NoteInput}
|
||||
value={transaction.notes}
|
||||
ref={noteRef}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{transaction.payment_type != 'paylater' ?
|
||||
<>
|
||||
<div className={styles.TotalContainer}>
|
||||
<span>Total:</span>
|
||||
<span>
|
||||
Rp {calculateTotalPrice(transaction.DetailedTransactions)}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
:
|
||||
|
||||
<h5
|
||||
className={`${styles.DeclineButton}`}
|
||||
>
|
||||
Open bill disetujui, silahkan lanjutkan pemesanan anda
|
||||
</h5>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
139
src/pages/Transaction_item.js
Normal file
139
src/pages/Transaction_item.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import styles from "./Transactions.module.css";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ColorRing } from "react-loader-spinner";
|
||||
import {
|
||||
getTransaction,
|
||||
confirmTransaction,
|
||||
cancelTransaction,
|
||||
} from "../helpers/transactionHelpers";
|
||||
import { getTables } from "../helpers/tableHelper";
|
||||
import TableCanvas from "../components/TableCanvas";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
export default function Transactions({ propsShopId, sendParam, deviceType, handleMoveToTransaction, shopImg }) {
|
||||
const { shopId, tableId } = useParams();
|
||||
if (sendParam) sendParam({ shopId, tableId });
|
||||
|
||||
const [tables, setTables] = useState([]);
|
||||
const [selectedTable, setSelectedTable] = useState(null);
|
||||
const [isPaymentLoading, setIsPaymentLoading] = useState(false);
|
||||
const [searchParams] = useSearchParams();
|
||||
const [transaction, setTransaction] = useState(null);
|
||||
const noteRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const transactionId = searchParams.get("transactionId") || "";
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTransaction = await getTransaction(transactionId);
|
||||
setTransaction(fetchedTransaction);
|
||||
console.log(fetchedTransaction); // Log the fetched transaction
|
||||
} catch (error) {
|
||||
console.error("Error fetching transaction:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const waitForLocalStorage = async () => {
|
||||
while (localStorage.getItem("auth") === null) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second
|
||||
}
|
||||
};
|
||||
|
||||
const initialize = async () => {
|
||||
await waitForLocalStorage();
|
||||
await fetchData();
|
||||
};
|
||||
|
||||
initialize();
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTables = await getTables(shopId || propsShopId);
|
||||
setTables(fetchedTables);
|
||||
} catch (error) {
|
||||
console.error("Error fetching tables:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [shopId || propsShopId]);
|
||||
|
||||
const calculateTotalPrice = (detailedTransactions) => {
|
||||
return detailedTransactions.reduce((total, dt) => {
|
||||
return total + dt.qty * (dt.promoPrice ? dt.promoPrice : dt.price);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const autoResizeTextArea = (textarea) => {
|
||||
if (textarea) {
|
||||
textarea.style.height = "auto"; // Reset height
|
||||
textarea.style.height = `${textarea.scrollHeight}px`; // Set new height
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (noteRef.current) {
|
||||
autoResizeTextArea(noteRef.current);
|
||||
}
|
||||
}, [transaction?.notes]);
|
||||
|
||||
return (
|
||||
<div className={styles.Transaction}>
|
||||
|
||||
<div className={styles.TransactionListContainer}>
|
||||
{transaction && (
|
||||
<div
|
||||
key={transaction.transactionId}
|
||||
className={styles.RoundedRectangle}
|
||||
onClick={() =>
|
||||
setSelectedTable(transaction.Table || { tableId: 0 })
|
||||
}
|
||||
>
|
||||
<ul>
|
||||
{transaction.DetailedTransactions.map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
{transaction.serving_type === "pickup"
|
||||
? "Ambil sendiri"
|
||||
: `Diantar ke ${transaction.Table ? transaction.Table.tableNo : "N/A"
|
||||
}`}
|
||||
</h2>
|
||||
{transaction.notes != "" && (
|
||||
<>
|
||||
<div className={styles.NoteContainer}>
|
||||
<span>Note :</span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<div className={styles.NoteContainer}>
|
||||
<textarea
|
||||
className={styles.NoteInput}
|
||||
value={transaction.notes}
|
||||
ref={noteRef}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className={styles.TotalContainer}>
|
||||
<span>Total:</span>
|
||||
<span>
|
||||
Rp {calculateTotalPrice(transaction.DetailedTransactions)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -118,6 +118,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
}
|
||||
}, [transaction?.notes]);
|
||||
|
||||
|
||||
return (
|
||||
<div className={styles.Transaction}>
|
||||
|
||||
@@ -138,9 +139,9 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
<p>ID Transaksi: {transaction.transactionId}</p>
|
||||
<p>Pembayaran: {transaction.payment_type}</p>
|
||||
<p>{transaction.serving_type === "pickup"
|
||||
? "Ambil sendiri"
|
||||
: `Diantar ke ${transaction.Table ? transaction.Table.tableNo : "N/A"
|
||||
}`}</p>
|
||||
? "Ambil sendiri"
|
||||
: `Diantar ke ${transaction.Table ? transaction.Table.tableNo : "N/A"
|
||||
}`}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -153,13 +154,27 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
</div>
|
||||
<div className={styles.RibbonBanner}></div>
|
||||
<ul>
|
||||
{transaction.DetailedTransactions.map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty} x Rp{" "}
|
||||
{detail.promoPrice ? detail.promoPrice : detail.Item.price}
|
||||
</li>
|
||||
{transaction.DetailedTransactions.map((detail, index) => (
|
||||
<>
|
||||
{detail.additionalNumber > transaction.DetailedTransactions[index - 1]?.additionalNumber &&
|
||||
|
||||
<div style={{marginTop: '10px'}} key={detail.detailedTransactionId}>
|
||||
tambah -----
|
||||
</div>
|
||||
}
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
</li>
|
||||
</>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div
|
||||
onClick={() => {
|
||||
localStorage.setItem('lastTransaction', JSON.stringify(transaction));
|
||||
}} className={styles["addNewItem"]}>Tambah pesanan</div>
|
||||
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
{transaction.serving_type === "pickup"
|
||||
? "Ambil sendiri"
|
||||
@@ -189,6 +204,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
Rp {calculateTotalPrice(transaction.DetailedTransactions)}
|
||||
</span>
|
||||
</div>
|
||||
{transaction.payment_type != 'paylater' &&
|
||||
<div className={styles.TotalContainer}>
|
||||
<button
|
||||
className={styles.PayButton}
|
||||
@@ -201,21 +217,16 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
|
||||
>
|
||||
{isPaymentLoading ? (
|
||||
<ColorRing height="50" width="50" color="white" />
|
||||
) : transaction.confirmed === 1 ? (
|
||||
"Konfirmasi telah bayar" // Display "Confirm has paid" if the transaction is confirmed (1)
|
||||
) : transaction.confirmed === -1 ? (
|
||||
"Ditolak" // Display "Declined" if the transaction is declined (-1)
|
||||
) : transaction.confirmed === -2 ? (
|
||||
"Dibatalkan" // Display "Declined" if the transaction is declined (-1)
|
||||
) : transaction.confirmed === 2 ? (
|
||||
"Konfirmasi pesanan siap" // Display "Item ready" if the transaction is ready (2)
|
||||
) : transaction.confirmed === 3 ? (
|
||||
"Transaction success" // Display "Item ready" if the transaction is ready (2)
|
||||
) : (
|
||||
"Batalkan" // Display "Confirm availability" if the transaction is not confirmed (0)
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,57 +1,241 @@
|
||||
import React from "react";
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import styles from "./Transactions.module.css";
|
||||
import { requestNotificationPermission } from '../services/notificationService'; // Import the notification service
|
||||
import { useParams } from "react-router-dom";
|
||||
import { ColorRing } from "react-loader-spinner";
|
||||
import ButtonWithReplica from "../components/ButtonWithReplica";
|
||||
import {
|
||||
getTransaction,
|
||||
confirmTransaction,
|
||||
handleClaimHasPaid,
|
||||
declineTransaction,
|
||||
cancelTransaction,
|
||||
} from "../helpers/transactionHelpers";
|
||||
import { getTables } from "../helpers/tableHelper";
|
||||
import TableCanvas from "../components/TableCanvas";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
export default function Transaction_pending({ setModal }) {
|
||||
// const containerStyle = {
|
||||
// display: "flex",
|
||||
// justifyContent: "center",
|
||||
// alignItems: "center",
|
||||
// width: "100%",
|
||||
// height: "100%",
|
||||
// backgroundColor: "",
|
||||
// };
|
||||
export default function Transactions({
|
||||
propsShopId,
|
||||
sendParam,
|
||||
deviceType,
|
||||
paymentUrl,
|
||||
}) {
|
||||
const { shopId, tableId } = useParams();
|
||||
if (sendParam) sendParam({ shopId, tableId });
|
||||
|
||||
const handleNotificationClick = async () => {
|
||||
const permission = await requestNotificationPermission();
|
||||
const [tables, setTables] = useState([]);
|
||||
const [selectedTable, setSelectedTable] = useState(null);
|
||||
const [isPaymentLoading, setIsPaymentLoading] = useState(false);
|
||||
const [searchParams] = useSearchParams();
|
||||
const [transaction, setTransaction] = useState(null);
|
||||
const noteRef = useRef(null);
|
||||
const [isPaymentOpen, setIsPaymentOpen] = useState(false);
|
||||
|
||||
if (permission === "granted") {
|
||||
console.log("Notification permission granted.");
|
||||
// Set up notifications or show a success modal
|
||||
} else if (permission === "denied") {
|
||||
console.error("Notification permission denied.");
|
||||
setModal('blocked_notification'); // Show modal for blocked notifications
|
||||
useEffect(() => {
|
||||
const transactionId = searchParams.get("transactionId") || "";
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTransaction = await getTransaction(transactionId);
|
||||
if (fetchedTransaction.payment_type == 'paylater' && deviceType == 'guestDevice') localStorage.setItem('lastTransaction', JSON.stringify(fetchedTransaction));
|
||||
setTransaction(fetchedTransaction);
|
||||
console.log(fetchedTransaction); // Log the fetched transaction
|
||||
} catch (error) {
|
||||
console.error("Error fetching transaction:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const waitForLocalStorage = async () => {
|
||||
while (localStorage.getItem("auth") === null) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second
|
||||
}
|
||||
};
|
||||
|
||||
const initialize = async () => {
|
||||
await waitForLocalStorage();
|
||||
await fetchData();
|
||||
};
|
||||
|
||||
initialize();
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const fetchedTables = await getTables(shopId || propsShopId);
|
||||
setTables(fetchedTables);
|
||||
} catch (error) {
|
||||
console.error("Error fetching tables:", error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [shopId || propsShopId]);
|
||||
|
||||
const calculateTotalPrice = (detailedTransactions) => {
|
||||
return detailedTransactions.reduce((total, dt) => {
|
||||
return total + dt.qty * (dt.promoPrice ? dt.promoPrice : dt.price);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const handleConfirm = async (transactionId) => {
|
||||
if (isPaymentLoading) return;
|
||||
setIsPaymentLoading(true);
|
||||
try {
|
||||
const c = await handleClaimHasPaid(transactionId);
|
||||
if (c) {
|
||||
setTransaction({
|
||||
...transaction,
|
||||
confirmed: c.confirmed,
|
||||
paymentClaimed: true,
|
||||
});
|
||||
console.log(transaction);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error processing payment:", error);
|
||||
} finally {
|
||||
setIsPaymentLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDecline = async (transactionId) => {
|
||||
if (isPaymentLoading) return;
|
||||
setIsPaymentLoading(true);
|
||||
try {
|
||||
const c = await cancelTransaction(transactionId);
|
||||
} catch (error) {
|
||||
console.error("Error processing payment:", error);
|
||||
} finally {
|
||||
setIsPaymentLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const autoResizeTextArea = (textarea) => {
|
||||
if (textarea) {
|
||||
textarea.style.height = "auto"; // Reset height
|
||||
textarea.style.height = `${textarea.scrollHeight}px`; // Set new height
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (noteRef.current) {
|
||||
autoResizeTextArea(noteRef.current);
|
||||
}
|
||||
}, [transaction?.notes]);
|
||||
|
||||
return (
|
||||
<div className={styles.Transaction}>
|
||||
<div style={{ marginTop: "30px", textAlign: "center" }}>
|
||||
<h2>Transaction Success</h2>
|
||||
<img
|
||||
className={styles.expression}
|
||||
src="https://i.imgur.com/sgvMI02.png"
|
||||
alt="Success"
|
||||
/>
|
||||
<p style={{ marginTop: "20px", color: "white" }}>
|
||||
Do you want to get notifications when your item is ready?
|
||||
</p>
|
||||
<button
|
||||
onClick={handleNotificationClick}
|
||||
style={{
|
||||
marginTop: "10px",
|
||||
padding: "10px 20px",
|
||||
fontSize: "16px",
|
||||
cursor: "pointer",
|
||||
backgroundColor: "#4CAF50",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "5px",
|
||||
}}
|
||||
<div className={styles.TransactionListContainer} style={{ overflow: 'hidden' }}>
|
||||
{transaction && (
|
||||
<div
|
||||
key={transaction.transactionId}
|
||||
className={styles.RoundedRectangle}
|
||||
onClick={() =>
|
||||
setSelectedTable(transaction.Table || { tableId: 0 })
|
||||
}
|
||||
>
|
||||
yes
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={styles['receipt-header']}>
|
||||
<div>
|
||||
<svg
|
||||
height="60px"
|
||||
width="60px"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 506.4 506.4"
|
||||
xmlSpace="preserve"
|
||||
fill="#000000"
|
||||
style={{marginTop: '12px', marginBottom: '12px'}}
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
|
||||
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<circle style={{ fill: '#54B265' }} cx="253.2" cy="253.2" r="249.2" />
|
||||
<path
|
||||
style={{ fill: '#F4EFEF' }}
|
||||
d="M372.8,200.4l-11.2-11.2c-4.4-4.4-12-4.4-16.4,0L232,302.4l-69.6-69.6c-4.4-4.4-12-4.4-16.4,0 L134.4,244c-4.4,4.4-4.4,12,0,16.4l89.2,89.2c4.4,4.4,12,4.4,16.4,0l0,0l0,0l10.4-10.4l0.8-0.8l121.6-121.6 C377.2,212.4,377.2,205.2,372.8,200.4z"
|
||||
></path>
|
||||
<path d="M253.2,506.4C113.6,506.4,0,392.8,0,253.2S113.6,0,253.2,0s253.2,113.6,253.2,253.2S392.8,506.4,253.2,506.4z M253.2,8 C118,8,8,118,8,253.2s110,245.2,245.2,245.2s245.2-110,245.2-245.2S388.4,8,253.2,8z"></path>
|
||||
<path d="M231.6,357.2c-4,0-8-1.6-11.2-4.4l-89.2-89.2c-6-6-6-16,0-22l11.6-11.6c6-6,16.4-6,22,0l66.8,66.8L342,186.4 c2.8-2.8,6.8-4.4,11.2-4.4c4,0,8,1.6,11.2,4.4l11.2,11.2l0,0c6,6,6,16,0,22L242.8,352.4C239.6,355.6,235.6,357.2,231.6,357.2z M154,233.6c-2,0-4,0.8-5.6,2.4l-11.6,11.6c-2.8,2.8-2.8,8,0,10.8l89.2,89.2c2.8,2.8,8,2.8,10.8,0l132.8-132.8c2.8-2.8,2.8-8,0-10.8 l-11.2-11.2c-2.8-2.8-8-2.8-10.8,0L234.4,306c-1.6,1.6-4,1.6-5.6,0l-69.6-69.6C158,234.4,156,233.6,154,233.6z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div className={styles['receipt-info']}>
|
||||
<h3>Transaksi Berhasil</h3>
|
||||
<p>Transaction ID: {transaction.transactionId}</p>
|
||||
<p>Payment Type: {transaction.payment_type}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{(isPaymentOpen
|
||||
? transaction.DetailedTransactions.slice(0, 2)
|
||||
: transaction.DetailedTransactions
|
||||
).map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h2 className={styles["Transactions-detail"]}>
|
||||
{transaction.serving_type === "pickup"
|
||||
? "Ambil sendiri"
|
||||
: `Diantar ke ${transaction.Table ? transaction.Table.tableNo : "N/A"
|
||||
}`}
|
||||
</h2>
|
||||
|
||||
{(transaction.notes !== "") && (
|
||||
<>
|
||||
<div
|
||||
className={styles.NoteContainer}
|
||||
style={{
|
||||
visibility: transaction.notes == "" ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<span>Note :</span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={styles.NoteContainer}
|
||||
style={{
|
||||
visibility: transaction.notes == "" ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<textarea
|
||||
className={styles.NoteInput}
|
||||
value={transaction.notes}
|
||||
ref={noteRef}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{transaction.payment_type != 'paylater' ?
|
||||
<>
|
||||
<div className={styles.TotalContainer}>
|
||||
<span>Total:</span>
|
||||
<span>
|
||||
Rp {calculateTotalPrice(transaction.DetailedTransactions)}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
:
|
||||
|
||||
<h5
|
||||
className={`${styles.DeclineButton}`}
|
||||
>
|
||||
Open bill disetujui, silahkan lanjutkan pemesanan anda
|
||||
</h5>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,10 +11,18 @@ import {
|
||||
import { getTables } from "../helpers/tableHelper";
|
||||
import TableCanvas from "../components/TableCanvas";
|
||||
|
||||
export default function Transactions({ shopId, propsShopId, sendParam, deviceType }) {
|
||||
import dayjs from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
|
||||
|
||||
export default function Transactions({ shop, shopId, propsShopId, sendParam, deviceType }) {
|
||||
const { shopIdentifier, tableId } = useParams();
|
||||
if (sendParam) sendParam({ shopIdentifier, tableId });
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
const [tables, setTables] = useState([]);
|
||||
const [selectedTable, setSelectedTable] = useState(null);
|
||||
const [transactions, setTransactions] = useState([]);
|
||||
@@ -86,6 +94,12 @@ export default function Transactions({ shopId, propsShopId, sendParam, deviceTyp
|
||||
return total + dt.qty * (dt.promoPrice ? dt.promoPrice : dt.price);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const calculateAllTransactionsTotal = (transactions) => {
|
||||
return transactions.reduce((grandTotal, transaction) => {
|
||||
return grandTotal + calculateTotalPrice(transaction.DetailedTransactions);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
const handleConfirm = async (transactionId) => {
|
||||
setIsPaymentLoading(true);
|
||||
@@ -132,7 +146,8 @@ export default function Transactions({ shopId, propsShopId, sendParam, deviceTyp
|
||||
return (
|
||||
<div className={styles.Transactions}>
|
||||
<div style={{ marginTop: "30px" }}></div>
|
||||
<h2 className={styles["Transactions-title"]}>Daftar transaksi</h2>
|
||||
<h2 className={styles["Transactions-title"]}>Daftar transaksi
|
||||
Rp {calculateAllTransactionsTotal(transactions)} </h2>
|
||||
<div style={{ marginTop: "30px" }}></div>
|
||||
{/* <TableCanvas tables={tables} selectedTable={selectedTable} /> */}
|
||||
<div className={styles.TransactionListContainer} style={{ padding: '0 20px 0 20px' }}>
|
||||
@@ -193,6 +208,7 @@ export default function Transactions({ shopId, propsShopId, sendParam, deviceTyp
|
||||
|
||||
<p>Transaction ID: {transaction.transactionId}</p>
|
||||
<p>Payment Type: {transaction.payment_type}</p>
|
||||
<p>{dayjs.utc(transaction.createdAt).tz(shop.timezone).format('YYYY-MM-DD HH:mm:ss')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles['dotted-line']}>
|
||||
@@ -214,8 +230,8 @@ export default function Transactions({ shopId, propsShopId, sendParam, deviceTyp
|
||||
<ul>
|
||||
{transaction.DetailedTransactions.map((detail) => (
|
||||
<li key={detail.detailedTransactionId}>
|
||||
<span>{detail.Item.name}</span> - {detail.qty} x Rp{" "}
|
||||
{detail.promoPrice ? detail.promoPrice : detail.price}
|
||||
<span>{detail.Item.name}</span> - {detail.qty < 1 ? 'tidak tersedia' : `${detail.qty} x Rp
|
||||
${detail.promoPrice ? detail.promoPrice : detail.price}`}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
font-family: "Plus Jakarta Sans", sans-serif;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-size: 15px; /* Adjusted for better readability */
|
||||
font-size: 12px; /* Adjusted for better readability */
|
||||
padding: 12px 16px; /* Added padding for a better look */
|
||||
border-radius: 50px;
|
||||
background-color: rgba(88, 55, 50, 1);
|
||||
@@ -172,7 +172,7 @@
|
||||
.RibbonBanner {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: -31px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
left: -18px;
|
||||
@@ -371,4 +371,28 @@
|
||||
margin: 3px;
|
||||
background-color: white;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
.itemContainer {
|
||||
display: flex;
|
||||
}
|
||||
.plusNegative {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
transform: rotate(45deg);
|
||||
margin: 4px 10px 0px 10px;
|
||||
}
|
||||
.plusNegative2 {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.addNewItem{
|
||||
width: 100%;
|
||||
height: 27px;
|
||||
background-color: rgb(115, 165, 133);
|
||||
border-radius: 11px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
line-height: 27px;
|
||||
}
|
||||
41
src/pages/Unavailable.js
Normal file
41
src/pages/Unavailable.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import React from "react";
|
||||
import styles from "./Transactions.module.css";
|
||||
import { requestNotificationPermission } from '../services/notificationService'; // Import the notification service
|
||||
|
||||
export default function Transaction_pending({ close }) {
|
||||
// const containerStyle = {
|
||||
// display: "flex",
|
||||
// justifyContent: "center",
|
||||
// alignItems: "center",
|
||||
// width: "100%",
|
||||
// height: "100%",
|
||||
// backgroundColor: "",
|
||||
// };
|
||||
|
||||
return (
|
||||
<div className={styles.Transaction}>
|
||||
<div style={{ textAlign: "center", padding: '10px' }}>
|
||||
|
||||
<h2>Kedai ini sedang tidak tersedia saat ini.</h2>
|
||||
<p style={{ marginTop: "20px", color: "black" }}>
|
||||
|
||||
</p>
|
||||
<button
|
||||
onClick={close}
|
||||
style={{
|
||||
marginTop: "10px",
|
||||
padding: "10px 20px",
|
||||
fontSize: "16px",
|
||||
cursor: "pointer",
|
||||
backgroundColor: "#4CAF50",
|
||||
color: "#fff",
|
||||
border: "none",
|
||||
borderRadius: "20px",
|
||||
}}
|
||||
>
|
||||
Tutup
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user