This commit is contained in:
Vassshhh
2025-08-27 10:11:42 +07:00
parent 40830ee48c
commit 4fa272875f
17 changed files with 1694 additions and 721 deletions

View File

@@ -10,7 +10,6 @@ import {
} from "react-router-dom";
import socket from "./services/socketService";
import Dashboard from "./pages/Dashboard";
import ScanMeja from "./pages/ScanMeja";
import CafePage from "./pages/CafePage";
@@ -27,7 +26,7 @@ import { getTableByCode } from "./helpers/tableHelper.js";
import {
getTransactionsFromCafe,
checkIsMyTransaction
checkIsMyTransaction,
} from "./helpers/transactionHelpers";
import {
getConnectedGuestSides,
@@ -45,7 +44,7 @@ import {
} from "./helpers/subscribeHelpers.js";
import Modal from "./components/Modal"; // Import your modal component
import { requestNotificationPermission } from './services/notificationService'; // Import the notification service
import { requestNotificationPermission } from "./services/notificationService"; // Import the notification service
function App() {
const location = useLocation();
@@ -73,35 +72,32 @@ function App() {
const [newTransaction, setNewTransaction] = useState({});
const queryParams = new URLSearchParams(location.search);
const tokenParams = queryParams.get("token");
if(tokenParams) localStorage.setItem('auth', tokenParams)
if (tokenParams) localStorage.setItem("auth", tokenParams);
const validTransactionStates = [
'new_transaction',
'transaction_canceled',
'transaction_pending',
'transaction_confirmed',
'payment_claimed',
'transaction_success',
'transaction_end',
'transaction_failed',
"new_transaction",
"transaction_canceled",
"transaction_pending",
"transaction_confirmed",
"payment_claimed",
"transaction_success",
"transaction_end",
"transaction_failed",
];
const calculateTotalsFromLocalStorage = () => {
const { totalCount, totalPrice } = calculateTotals(shopId);
setTotalItemsCount(totalCount);
setTotalPrice(totalPrice);
// If 'lastTransaction' exists, proceed
const lastTransaction = JSON.parse(localStorage.getItem("lastTransaction"));
console.log(lastTransaction);
if (lastTransaction != null) {
console.log(lastTransaction)
console.log(lastTransaction);
setLastTransaction(lastTransaction);
}
};
@@ -145,9 +141,13 @@ function App() {
return;
}
setModal('transaction_confirmed', { transactionId: lastTransaction.transactionId })
const myLastTransaction = await checkIsMyTransaction(lastTransaction.transactionId);
console.log(myLastTransaction)
setModal("transaction_confirmed", {
transactionId: lastTransaction.transactionId,
});
const myLastTransaction = await checkIsMyTransaction(
lastTransaction.transactionId
);
console.log(myLastTransaction);
if (myLastTransaction.isMyTransaction) {
setLastTransaction(lastTransaction);
} else {
@@ -155,7 +155,6 @@ function App() {
}
};
const handleSetParam = async ({ shopIdentifier, tableCode }) => {
setShopIdentifier(shopIdentifier);
@@ -170,12 +169,14 @@ function App() {
const fetchData = async () => {
console.log("gettingItems");
try {
const { response, cafe, data } = await getItemTypesWithItems(shopIdentifier);
const { response, cafe, data } = await getItemTypesWithItems(
shopIdentifier
);
if (response.status === 200) {
setShopId(cafe.cafeId)
setShopId(cafe.cafeId);
setShop(cafe);
setShopItems(data);
console.log(data)
console.log(data);
// Filter out unavailable items
const filteredData = data
.map((itemType) => ({
@@ -241,7 +242,7 @@ function App() {
// Call `setModal` with content and parameters
setModal("transaction_pending", data);
localStorage.setItem('cart', []);
localStorage.setItem("cart", []);
calculateTotalsFromLocalStorage();
});
@@ -250,7 +251,7 @@ function App() {
console.log(JSON.stringify(data));
setModal("transaction_confirmed", data);
localStorage.setItem('cart', []);
localStorage.setItem("cart", []);
// const startTime = Date.now(); // Capture the start time
// const timeout = 10000; // 10 seconds timeout in milliseconds
@@ -266,7 +267,9 @@ function App() {
// }
// If 'lastTransaction' exists, proceed
const lastTransaction = JSON.parse(localStorage.getItem("lastTransaction"));
const lastTransaction = JSON.parse(
localStorage.getItem("lastTransaction")
);
console.log(lastTransaction);
if (lastTransaction != null) {
@@ -274,7 +277,6 @@ function App() {
}
});
socket.on("transaction_success", async (data) => {
console.log("transaction notification");
setModal("transaction_success", data);
@@ -316,7 +318,6 @@ function App() {
}
});
const handleNotificationClick = async () => {
const permission = await requestNotificationPermission();
@@ -325,7 +326,7 @@ function App() {
// Set up notifications or show a success modal
} else {
console.error("Notification permission denied.");
setModal('blocked_notification'); // Show modal for blocked notifications
setModal("blocked_notification"); // Show modal for blocked notifications
}
};
@@ -334,10 +335,21 @@ function App() {
// Check current permission
const searchParams = new URLSearchParams(location.search);
let searchModal = searchParams.get("modal") || ''; // Get transactionId or set it to empty string
let searchModal = searchParams.get("modal") || ""; // Get transactionId or set it to empty string
if (permission !== "granted" && searchModal == '') {
setModal("message", { captMessage: 'Notifikasi tidak aktif', descMessage: 'Aktifkan notifikasi supaya kamu tetap dapat info pesanan, meski sedang buka aplikasi lain.', yesText: 'Aktifkan', noText: 'Tutup' }, null, handleNotificationClick);
if (permission !== "granted" && searchModal == "") {
setModal(
"message",
{
captMessage: "Notifikasi tidak aktif",
descMessage:
"Aktifkan notifikasi supaya kamu tetap dapat info pesanan, meski sedang buka aplikasi lain.",
yesText: "Aktifkan",
noText: "Tutup",
},
null,
handleNotificationClick
);
}
};
@@ -346,10 +358,15 @@ function App() {
removeLocalStorage("auth");
setDeviceType("guestDevice");
} else {
console.log(data)
if (data.data.user.cafeId != null) navigate(`/${data.data.user.cafeIdentityName}`, { replace: true });
console.log(data);
if (data.data.user.cafeId != null)
navigate(`/${data.data.user.cafeIdentityName}`, { replace: true });
setUser(data.data.user);
if (data.data.latestOpenBillTransaction != null) localStorage.setItem('lastTransaction', JSON.stringify(data.data.latestOpenBillTransaction))
if (data.data.latestOpenBillTransaction != null)
localStorage.setItem(
"lastTransaction",
JSON.stringify(data.data.latestOpenBillTransaction)
);
if (
data.data.user.password == "unsetunsetunset" &&
localStorage.getItem("settings")
@@ -393,7 +410,6 @@ function App() {
socket.on("updateQueue", ({ queue }) => {
setQueue(queue); // Only set the queue if it's a valid non-empty array
console.log("Updated Queue:", queue); // Log the valid queue
});
return () => {
@@ -402,7 +418,6 @@ function App() {
}, [socket, shopId]);
async function checkIfStillViewingOtherTransaction(data) {
console.log("transaction notification");
console.log(modalContent);
@@ -412,21 +427,21 @@ function App() {
// Get current URL's search parameters inside the socket event handler
const searchParams = new URLSearchParams(location.search);
let transaction_info = searchParams.get("transactionId") || ''; // Get transactionId or set it to empty string
let transaction_info = searchParams.get("transactionId") || ""; // Get transactionId or set it to empty string
if(response[0].transactionId != transaction_info) transactionList.current = response;
if (response[0].transactionId != transaction_info)
transactionList.current = response;
let depthh = transactionList.current.findIndex(
item => item.transactionId.toString() === transaction_info.toString()
(item) => item.transactionId.toString() === transaction_info.toString()
);
if (transaction_info != response[0].transactionId)
setDepth(depthh);
if (transaction_info != response[0].transactionId) setDepth(depthh);
else setModal("new_transaction", data);
console.log(transaction_info == response[0].transactionId)
console.log(transaction_info == response[0].transactionId);
// If transaction_info is an empty string, set the modal
if (transaction_info.toString() == '') return false;
if (transaction_info.toString() == "") return false;
else return true;
}
@@ -436,8 +451,9 @@ function App() {
console.log("transaction notification");
setNewTransaction(data);
if(!location.pathname.endsWith('/transactions')){
const isViewingOtherTransaction = await checkIfStillViewingOtherTransaction(data);
if (!location.pathname.endsWith("/transactions")) {
const isViewingOtherTransaction =
await checkIfStillViewingOtherTransaction(data);
// If transaction_info is an empty string, set the modal
if (!isViewingOtherTransaction) {
setModal("new_transaction", data);
@@ -458,8 +474,9 @@ function App() {
console.log("transaction notification");
setNewTransaction(data);
if(location.pathname != '/transactions'){
const isViewingOtherTransaction = await checkIfStillViewingOtherTransaction(data);
if (location.pathname != "/transactions") {
const isViewingOtherTransaction =
await checkIfStillViewingOtherTransaction(data);
// If transaction_info is an empty string, set the modal
if (!isViewingOtherTransaction) {
setModal("new_transaction", data);
@@ -480,27 +497,29 @@ function App() {
console.log(from);
// Find the current index based on the 'from' transactionId
const currentIndex = transactionList.current.findIndex(item => item.transactionId == from);
const currentIndex = transactionList.current.findIndex(
(item) => item.transactionId == from
);
// Determine the new transactionId based on the direction
let newTransactionId;
if (direction === 'next') {
if (direction === "next") {
// If we're not at the last transaction, get the next transactionId
newTransactionId = currentIndex < transactionList.current.length - 1
newTransactionId =
currentIndex < transactionList.current.length - 1
? transactionList.current[currentIndex + 1].transactionId
: from; // If already at the end, stay on the current transactionId
} else if (direction === 'previous') {
} else if (direction === "previous") {
setDepth(currentIndex - 1);
// If we're not at the first transaction, get the previous transactionId
newTransactionId = currentIndex > 0
newTransactionId =
currentIndex > 0
? transactionList.current[currentIndex - 1].transactionId
: from; // If already at the beginning, stay on the current transactionId
}
// Log the new transactionId
console.log('New Transaction ID:', newTransactionId);
console.log("New Transaction ID:", newTransactionId);
// Update the URL with the new transactionId using navigate
navigate(`?transactionId=${newTransactionId}`, { replace: true });
@@ -510,7 +529,6 @@ function App() {
// setModalContent({ cafeId: shopId, transactionId: newTransactionId });
}
const handleModalFromURL = () => {
const queryParams = new URLSearchParams(location.search);
const modal = queryParams.get("modal");
@@ -542,8 +560,8 @@ function App() {
document.body.style.overflow = "hidden";
setIsModalOpen(true);
setModalContent(content)
console.log(onCloseFunction)
setModalContent(content);
console.log(onCloseFunction);
if (onCloseFunction) {
setOnModalCloseFunction(() => onCloseFunction); // Store the close function
@@ -560,7 +578,8 @@ function App() {
const closeModal = (closeTheseContent = []) => {
if (
Array.isArray(closeTheseContent) &&
(closeTheseContent.length === 0 || closeTheseContent.includes(modalContent))
(closeTheseContent.length === 0 ||
closeTheseContent.includes(modalContent))
) {
setIsModalOpen(false);
setModalContent(null);
@@ -569,7 +588,8 @@ function App() {
const queryParams = new URLSearchParams(location.search);
// Clear all query parameters
queryParams.keys() && [...queryParams.keys()].forEach(key => {
queryParams.keys() &&
[...queryParams.keys()].forEach((key) => {
queryParams.delete(key);
});
@@ -578,7 +598,6 @@ function App() {
}
};
// useEffect(() => {
// const askNotificationPermission = async () => {
// let permission = Notification.permission;
@@ -637,11 +656,15 @@ function App() {
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/service-worker.js")
.then(registration => {
console.log("Service Worker registered with scope:", registration.scope);
navigator.serviceWorker
.register("/service-worker.js")
.then((registration) => {
console.log(
"Service Worker registered with scope:",
registration.scope
);
})
.catch(error => {
.catch((error) => {
console.error("Service Worker registration failed:", error);
});
}
@@ -702,6 +725,9 @@ function App() {
cartItemsLength={totalItemsCount}
totalPrice={totalPrice}
lastTransaction={lastTransaction}
shop={shop}
totalItemsCount={totalItemsCount}
deviceType={deviceType}
/>
</>
}

View File

@@ -35,11 +35,16 @@ const Title = styled.h2`
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 500;
font-style: normal;
font-size:${(props) => (props.HeaderSize)};
font-size: ${(props) => props.HeaderSize};
color: rgba(88, 55, 50, 1);
text-transform: uppercase;
@media (min-width: 768px) {
font-size: 2vw;
}
`;
const ProfileName = styled.h2`
position: absolute;
font-family: "Plus Jakarta Sans", sans-serif;
@@ -81,8 +86,8 @@ const gg = keyframes`
height: 60px;
}
100% {
top: 45px;
right: 51px;
top: 5px;
right: 20px;
width: 200px;
height: 40px;
}
@@ -90,8 +95,8 @@ const gg = keyframes`
const ss = keyframes`
0% {
top: 45px;
right: 51px;
top: 5px;
right: 20px;
width: 200px;
height: 40px;
}
@@ -127,8 +132,8 @@ const g = keyframes`
height: 60px;
}
100% {
top: 28px;
right: 242px;
top: 34px;
right: 229px;
width: 40px;
height: 40px;
}
@@ -137,7 +142,7 @@ const g = keyframes`
const s = keyframes`
0% {
top: 28px;
right: 242px;
right: 229px;
width: 40px;
height: 40px;
}
@@ -151,14 +156,15 @@ const s = keyframes`
const grow = keyframes`
0% {
right: 12px;
right: 0px;
top: 0px;
width: 60px;
height: 60px;
border-top-left-radius: 50%;
border-bottom-left-radius: 50%;
}
100% {
right: 28px;
right: -17px;
width: 300px;
border-top-left-radius: 15px;
border-bottom-left-radius: 15px;
@@ -167,13 +173,14 @@ const grow = keyframes`
const shrink = keyframes`
0% {
right: 12px;
right: -17px;
width: 300px;
height: auto;
border-radius: 20px;
}
100% {
right: 28px;
right: 0px;
top: 0px;
width: 60px;
height: 60px;
border-radius: 50%;
@@ -182,7 +189,7 @@ const shrink = keyframes`
const Rectangle = styled.div`
overflow-y: hidden;
position: absolute;
top: 39px;
top: 9px;
right: 12px;
width: 200px;
max-height: 87vh; /* or another appropriate value */
@@ -331,7 +338,7 @@ const Header = ({
: HeaderText
: shopName}
</Title>
<div style={{ visibility: showProfile ? "visible" : "hidden" }}>
<div style={{ visibility: showProfile ? "visible" : "hidden", position: 'relative' }}>
<ProfileImage
src={shopImage && !shopImage.includes('undefined') ? shopImage || 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS-DjX_bGBax4NL14ULvkAdU4FP3FKoWXWu5w&s' : "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS-DjX_bGBax4NL14ULvkAdU4FP3FKoWXWu5w&s"}
alt="Profile"
@@ -355,7 +362,7 @@ const Header = ({
</Child>
)}
{user.roleId == 0 && (
<Child onClick={()=>setModal('create_coupon', {})}>Buat Voucher</Child>)}
<Child onClick={() => setModal('create_coupon', {})}>Buat Voucher</Child>)}
{shopId && user.roleId == 1 && (
<Child onClick={goToAdminCafes}>Dashboard</Child>)}
{shopId &&
@@ -367,14 +374,6 @@ const Header = ({
<Child>
{shopName}
</Child>
<Child>
Mode Edit &nbsp;
<Switch
borderRadius={0}
checked={isEditMode}
onChange={() => setIsEditMode(!isEditMode)}
/>
</Child>
<Child onClick={() => setModal("reports")}>Lihat laporan</Child>
<Child onClick={() => setModal("add_material")}>
Kelola stok

View File

@@ -29,6 +29,10 @@ const Item = ({
const [itemDescription, setItemDescription] = useState(initialDescription);
const fileInputRef = useRef(null);
const formatToRupiah = (value) => {
if (typeof value !== "number") return value;
return value.toLocaleString("id-ID");
};
useEffect(() => {
console.log(imageUrl);
console.log(selectedImage);
@@ -156,7 +160,6 @@ const Item = ({
</h3>
{forInvoice && (
<>
<p className={styles.multiplySymbol}>x</p>
<p className={styles.qtyInvoice}>{itemQty}</p>
</>
)}
@@ -208,12 +211,12 @@ const Item = ({
marginLeft: '1rem',
marginRight: '0.5rem',
marginTop: '0.125rem'
}}>{promoPrice}</span>
}}>{formatToRupiah(promoPrice)}</span>
<span style={{
marginTop: '0.125rem',
color: 'rgb(114, 114, 114)',
textDecoration: 'line-through'
}}>{initialPrice}</span>
}}>{formatToRupiah(initialPrice)}</span>
</div>
</>
:
@@ -223,7 +226,7 @@ const Item = ({
<span style={{
marginRight: '0.5rem',
marginTop: '0.125rem'
}}>{initialPrice}</span>
}}>{formatToRupiah(initialPrice)}</span>
</div>
</>
}
@@ -302,7 +305,7 @@ const Item = ({
))}
{forInvoice && (
<p className={styles.itemPriceInvoice}>Rp {itemQty * (promoPrice > 0? promoPrice : itemPrice)}</p>
<p className={styles.itemPriceInvoice}>Rp {itemQty * (promoPrice > 0? formatToRupiah(promoPrice) : formatToRupiah(itemPrice))}</p>
)}
</div>
{forCart && (

View File

@@ -13,9 +13,12 @@
margin-bottom: 5px;
color: rgba(88, 55, 50, 1);
font-size: 32px;
box-sizing: border-box; /* Include padding and border in the element's total width */
width: 100%; /* Ensure the item does not exceed the parent's width */
overflow: hidden; /* Prevent internal overflow */
box-sizing: border-box;
/* Include padding and border in the element's total width */
width: 100%;
/* Ensure the item does not exceed the parent's width */
overflow: hidden;
/* Prevent internal overflow */
padding-top: 10px;
margin-bottom: 5px;
}
@@ -24,7 +27,7 @@
/* border-top: 2px solid #00000017; */
}
.notLast{
.notLast {
padding-bottom: 10px;
border-bottom: 2px solid #00000017;
}
@@ -40,7 +43,8 @@
}
.itemInvoice:last-child {
margin-bottom: 0; /* Remove margin-bottom for the last child */
margin-bottom: 0;
/* Remove margin-bottom for the last child */
}
.itemImage {
@@ -102,7 +106,8 @@
.itemName {
font-family: "Plus Jakarta Sans", sans-serif;
font-style: normal;
width: calc(100% - 15px); /* Adjust the width to prevent overflow */
width: calc(100% - 15px);
/* Adjust the width to prevent overflow */
font-size: 5vw;
font-weight: 500;
margin-top: 0;
@@ -131,7 +136,8 @@
font-family: "Plus Jakarta Sans", sans-serif;
font-style: normal;
font-weight: 600;
width: calc(100% - 15px); /* Adjust the width to prevent overflow */
width: calc(100% - 15px);
/* Adjust the width to prevent overflow */
font-size: 3.3vw;
/* margin-bottom: 35px; */
margin-left: 5px;
@@ -175,7 +181,8 @@
font-family: "Plus Jakarta Sans", sans-serif;
font-style: normal;
font-weight: 600;
width: 30px; /* Adjust the width to prevent overflow */
width: 30px;
/* Adjust the width to prevent overflow */
font-size: 0.9rem;
margin-bottom: 10px;
text-align: center;
@@ -196,6 +203,7 @@
margin-top: 5px;
border-radius: 20px;
}
.grayscale {
filter: grayscale(100%);
}
@@ -203,6 +211,7 @@
.disabled {
color: gray;
}
.plusNegative {
width: 35px;
height: 35px;
@@ -217,6 +226,7 @@
left: -33px;
top: 21px;
}
.remove {
width: 25px;
height: 25px;
@@ -249,3 +259,267 @@
left: 15px;
right: 15px;
}
@media (min-width: 768px) {
.itemContainer {
display: flex;
flex-direction: column;
/* gap: 10px; */
}
.item {
display: flex;
align-items: stretch;
justify-content: space-between;
padding-left: 5px;
margin-top: 5px;
margin-bottom: 5px;
color: rgba(88, 55, 50, 1);
font-size: 32px;
box-sizing: border-box;
/* Include padding and border in the element's total width */
width: 100%;
/* Ensure the item does not exceed the parent's width */
overflow: hidden;
/* Prevent internal overflow */
padding-top: 10px;
margin-bottom: 5px;
}
.item:not(.itemInvoice) {
/* border-top: 2px solid #00000017; */
}
.notLast {
padding-bottom: 10px;
border-bottom: 2px solid #00000017;
}
.itemInvoice {
flex-direction: row;
align-items: center;
justify-content: space-around;
font-size: 18px;
margin-top: 0px;
margin-bottom: 0px;
padding-top: 0px;
}
.itemInvoice:last-child {
margin-bottom: 0;
/* Remove margin-bottom for the last child */
}
.itemImage {
width: 100%;
height: 100%;
}
.imageContainer {
position: relative;
width: 20%;
height: 20%;
border-radius: 12px;
object-fit: cover;
}
.overlay {
position: absolute;
top: 15px;
left: 8px;
right: 8px;
bottom: 15px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
display: flex;
justify-content: center;
align-items: center;
border-radius: 20px;
cursor: pointer;
transition: background-color 0.3s ease;
font-size: 3.3vw;
}
.overlay:hover {
background-color: rgba(0, 0, 0, 0.7);
}
.fileInput {
display: none;
}
.itemDetails {
display: flex;
flex-direction: column;
justify-content: space-between;
margin-left: 10px;
margin-right: 10px;
flex-grow: 1;
}
.itemInvoiceDetails {
display: flex;
flex-direction: column;
justify-content: space-between;
margin-left: 10px;
margin-top: -15px;
flex-grow: 1;
}
.itemName {
font-family: "Plus Jakarta Sans", sans-serif;
font-style: normal;
width: calc(100% - 15px);
/* Adjust the width to prevent overflow */
font-size: 5vw;
font-weight: 500;
margin-top: 0;
margin: 0 5px;
color: rgba(88, 55, 50, 1);
background-color: transparent;
text-transform: capitalize;
}
.itemInvoiceName {
width: calc(260% - 15px);
background-color: transparent;
font-size: 1.3rem;
font-weight: 500;
}
.multiplySymbol {
font-weight: 600;
}
.qtyInvoice {
font-weight: 500;
}
.itemPrice {
font-family: "Plus Jakarta Sans", sans-serif;
font-style: normal;
font-weight: 600;
width: calc(100% - 15px);
/* Adjust the width to prevent overflow */
font-size: 3.3vw;
/* margin-bottom: 35px; */
margin-left: 5px;
color: #3a3a3a;
background-color: transparent;
}
.itemPriceInvoice {
font-family: "Plus Jakarta Sans", sans-serif;
font-style: normal;
font-weight: 600;
font-size: 0.9rem;
margin-left: 5px;
color: #d9c61c;
text-align: right;
margin-top: 22px;
}
.itemQty {
display: flex;
align-items: center;
font-size: 0.9rem;
margin-left: 5px;
color: #a8c7a9;
fill: #a8c7a9;
height: 40px;
}
.itemQtyValue {
font-family: "Plus Jakarta Sans", sans-serif;
font-style: normal;
font-weight: 600;
margin-top: 19px;
margin-left: 1px;
margin-right: 1px;
width: 25px;
text-align: center;
}
.itemQtyInput {
font-family: "Plus Jakarta Sans", sans-serif;
font-style: normal;
font-weight: 600;
width: 30px;
/* Adjust the width to prevent overflow */
font-size: 0.9rem;
margin-bottom: 10px;
text-align: center;
background-color: transparent;
}
.addButton {
background-color: #ffffff;
border: 2px solid #a8c7a9;
/* border: none; */
display: inline-block;
font-size: 14px;
font-weight: 600;
cursor: pointer;
width: 87px;
height: 32px;
margin-left: 5px;
margin-top: 5px;
border-radius: 20px;
}
.grayscale {
filter: grayscale(100%);
}
.disabled {
color: gray;
}
.plusNegative {
width: 35px;
height: 35px;
margin: 2.5px 0 -0.5px 0px;
}
.plusNegative2 {
width: 84px;
height: 21px;
position: absolute;
transform: rotate(45deg);
left: -33px;
top: 21px;
}
.remove {
width: 25px;
height: 25px;
margin-top: -10px;
margin-right: 10px;
}
.itemInvoice .itemDetails {
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.itemInvoice .itemName,
.itemInvoice .itemPrice,
.itemInvoice .itemQty .qtyInvoice .multiplySymbol {
font-size: 0.9rem;
}
.blank {
border: 1px solid #000000;
}
.notblank {
border: 1px solid #ffffff00;
}
.createItem {
position: absolute;
left: 15px;
right: 15px;
}
}

View File

@@ -243,7 +243,7 @@ const ItemConfig = ({
<ThreeDots height={20} width={20} />
) : (
isBeingEdit ? 'Simpan' : 'Buat Item'
isBeingEdit ? 'Simpan' : 'Simpan'
)}
</div>

View File

@@ -9,6 +9,7 @@ export default function ItemType({
imageUrl,
selected,
rectangular,
typeLength
}) {
const inputRef = useRef(null);
const [namee, setName] = useState(name);
@@ -68,7 +69,7 @@ export default function ItemType({
: "item-type-nomargin"
]
}
style={{ zIndex: blank ? 301 : "inherit" }}
style={{ zIndex: blank ? 301 : "inherit", width: `calc(${100 / (Math.min(typeLength, 3) + 1)}% - 10px)` }}
>
<div
onClick={

View File

@@ -94,3 +94,102 @@
.noborder {
border: 1px solid #ffffff00;
}
@media (min-width: 768px){
.item-type {
width: calc(25% - 20px);
height: calc(30% - 20px);
margin: 1px 10px 0px;
overflow: visible;
text-align: center;
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.item-type-rectangular {
width: calc(100% - 20px);
height: calc(100% - 20px);
overflow: visible;
text-align: center;
align-items: center;
display: flex;
flex-direction: column;
justify-content: start;
position: relative; /* Ensure absolute positioning inside works */
}
.item-type-nomargin {
width: calc(25% - 20px);
height: calc(39% - 20px);
overflow: visible;
text-align: center;
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
position: relative; /* Ensure absolute positioning inside works */
}
.item-type-rect {
position: relative;
height: 30%;
width: 30%;
object-fit: cover;
border-radius: 15px;
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.item-type-name {
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 14px;
color: #333;
width: calc(25vw - 30px);
text-align: center;
background-color: transparent;
position: relative; /* Needed for positioning the button */
}
.item-type-image {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 15px;
}
.item-type-image-container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.item-type-image-input {
opacity: 0;
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
}
.item-type-create {
position: absolute;
top: 76%; /* Position below the input */
left: 50%;
transform: translateX(-50%);
margin-top: 10px; /* Space between input and button */
width: 20vw;
text-align: center; /* Center button text */
}
.border {
border: 1px solid #000000;
}
.noborder {
border: 1px solid #ffffff00;
}
}

View File

@@ -1,5 +1,4 @@
.item-type-lister {
width: 100vw;
overflow-x: auto;
white-space: nowrap;
padding: 3px 0px;
@@ -11,6 +10,7 @@
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
overflow-y: hidden;
width: 100%;
}
.item-type {

View File

@@ -141,13 +141,14 @@ const ItemTypeLister = ({
itemTypes.map(
(itemType) =>
(
itemType.itemList.length > 0 || (user && (user.user_id == shopOwnerId || user.cafeId == shopId))) && (
<ItemType
key={itemType.itemTypeId}
name={itemType.name}
imageUrl={getImageUrl(itemType.image)}
onClick={() => onFilterChange(itemType.itemTypeId)}
selected={filterId === itemType.itemTypeId}
typeLength={itemTypes.length}
/>
)
)}

View File

@@ -367,7 +367,7 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
const [text, setText] = useState("Menunggu musik favoritmu");
const textIndex = useRef(0);
const [messages, setMessages] = useState(["Menunggu musik favoritmu", "Klik untuk putar musik favoritmu"]);
const [messages, setMessages] = useState(["Menunggu musik favoritmu", "Upgrade to use"]);
useEffect(() => {
@@ -376,7 +376,7 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
currentSong != null && currentSong[0]?.trackId != 'kCGs5_oCtBE' && currentSong[0]?.trackId != 'O8eYd7oAZtA' && currentSong[0]?.name != undefined
? `${currentSong[0]?.name} - ${currentSong[0]?.artist}`
: "Menunggu musik favoritmu",
"Klik untuk putar musik favoritmu"
"Upgrade to use"
];
setMessages(newMessages);
@@ -421,7 +421,7 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
return (
<div className={`music-player`} style={{ marginBottom: `${viewing ? '-10px' : ''}` }}>
<div
onClick={toggleView}
// onClick={toggleView}
className="current-bgr"
style={{ backgroundImage: `url(${backgroundImage})` }}
// style={{ backgroundImage: `url(${videoSrc != "" ? '' : backgroundImage})` }}

View File

@@ -10,9 +10,14 @@ import {
import "../App.css";
import API_BASE_URL from '../config';
import API_BASE_URL from "../config";
import Watermark from "../components/Watermark";
import { getImageUrl, createItem, updateItem, moveItemType } from "../helpers/itemHelper.js";
import {
getImageUrl,
createItem,
updateItem,
moveItemType,
} from "../helpers/itemHelper.js";
import SearchInput from "../components/SearchInput";
import ItemTypeLister from "../components/ItemTypeLister";
import { MusicPlayer } from "../components/MusicPlayer";
@@ -23,11 +28,17 @@ import Switch from "react-switch";
import { ThreeDots } from "react-loader-spinner";
import { getLocalStorage, updateLocalStorage, removeLocalStorage } from "../helpers/localStorageHelpers";
import {
getLocalStorage,
updateLocalStorage,
removeLocalStorage,
} from "../helpers/localStorageHelpers";
import { unsubscribeUser } from "../helpers/subscribeHelpers.js";
import WelcomePage from "./WelcomePage.js";
import { useNavigationHelpers } from "../helpers/navigationHelpers";
import Cart from "./Cart";
function CafePage({
shopId,
table,
@@ -48,21 +59,21 @@ function CafePage({
queue,
cartItemsLength,
totalPrice,
lastTransaction
lastTransaction,
shop,
totalItemsCount,
deviceType,
}) {
const location = useLocation();
const [searchParams] = useSearchParams();
const token = searchParams.get("token");
const { shopIdentifier, tableCode } = useParams();
sendParam({ shopIdentifier, tableCode });
const {
goToCart,
goToTransactions
} = useNavigationHelpers(shopIdentifier, table.tableCode);
const { goToCart, goToTransactions } = useNavigationHelpers(
shopIdentifier,
table.tableCode
);
const navigate = useNavigate();
@@ -90,10 +101,20 @@ function CafePage({
// }
// }
// };
const [isTablet, setIsTablet] = useState(window.innerWidth >= 768);
useEffect(() => {
const handleResize = () => {
setIsTablet(window.innerWidth >= 768);
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
useEffect(() => {
if (window.gtag && shopIdentifier) {
window.gtag('event', 'page_view', {
window.gtag("event", "page_view", {
page_title: `Cafe - ${shopIdentifier}`,
page_location: window.location.href,
page_path: `/` + shopIdentifier,
@@ -116,22 +137,26 @@ function CafePage({
// checkWelcomePageConfig();
}, [welcomePageConfig]);
useEffect(() => {
function fetchData() {
console.log(user.user_id == shopOwnerId)
console.log(user.user_id == shopOwnerId);
setModal("create_item");
}
console.log(getLocalStorage('auth'))
console.log(getLocalStorage("auth"));
if (getLocalStorage("auth") != null) {
const executeFetch = async () => {
while (user.length == 0) {
await new Promise((resolve) => setTimeout(resolve, 100)); // Wait until the user is not null
}
console.log(user)
console.log('open')
if (user.length != 0 && user.user_id == shopOwnerId && shopItems.length == 0) fetchData();
console.log(user);
console.log("open");
if (
user.length != 0 &&
user.user_id == shopOwnerId &&
shopItems.length == 0
)
fetchData();
};
executeFetch();
}
@@ -154,13 +179,39 @@ function CafePage({
socket.on("joined-room", (response) => {
const { isSpotifyNeedLogin, isExceededDeadline } = response;
setNeedSpotifyLogin(isSpotifyNeedLogin);
if (isExceededDeadline) setModal("message", { captMessage: 'Kafe sedang tidak tersedia' });
if (isExceededDeadline)
setModal("message", { captMessage: "Kafe sedang tidak tersedia" });
setIsExceededDeadline(isExceededDeadline);
});
}
if (socket) fetchData();
}, [socket]);
useEffect(() => {
const isTablet = window.innerWidth >= 768;
const handleFirstClick = async () => {
if (
isTablet &&
document.fullscreenEnabled &&
!document.fullscreenElement
) {
try {
await document.documentElement.requestFullscreen();
document.removeEventListener("click", handleFirstClick);
} catch (err) {
console.warn("Gagal masuk fullscreen:", err);
}
}
};
// Tambahkan listener satu kali
document.addEventListener("click", handleFirstClick);
return () => {
document.removeEventListener("click", handleFirstClick);
};
}, []);
const handleGetStarted = () => {
setIsStarted(false);
@@ -175,16 +226,19 @@ function CafePage({
const newItems = [...shopItems];
let targetIndex;
if (direction === 'up' && index > 0) {
if (direction === "up" && index > 0) {
targetIndex = index - 1;
} else if (direction === 'down' && index < newItems.length - 1) {
} else if (direction === "down" && index < newItems.length - 1) {
targetIndex = index + 1;
}
console.log(index);
console.log(targetIndex);
if (targetIndex !== undefined) {
// Swap items
[newItems[index], newItems[targetIndex]] = [newItems[targetIndex], newItems[index]];
[newItems[index], newItems[targetIndex]] = [
newItems[targetIndex],
newItems[index],
];
newItems[index].order = targetIndex;
newItems[targetIndex].order = index;
@@ -192,9 +246,14 @@ function CafePage({
// Call the API to move the item type
try {
await moveItemType(itemTypeId, previousItems[targetIndex].itemTypeId, index, targetIndex);
await moveItemType(
itemTypeId,
previousItems[targetIndex].itemTypeId,
index,
targetIndex
);
} catch (error) {
console.error('Error moving item type:', error);
console.error("Error moving item type:", error);
// Revert the changes if the backend fails
setShopItems(previousItems);
}
@@ -224,11 +283,19 @@ function CafePage({
isFullscreen={true}
/>
) : ( */}
<div className={`Cafe ${isExceededDeadline ? 'grayscale' : ''}`}>
{API_BASE_URL != 'https://dev.api.kedaimaster.com' && API_BASE_URL != 'https://api.kedaimaster.com' &&
<div
className={`Cafe ${isExceededDeadline ? "grayscale" : ""}`}
style={{
display: isTablet ? "flex" : "block",
flexDirection: "row",
width: "100%",
}}
>
{API_BASE_URL != "https://dev.api.kedaimaster.com" &&
API_BASE_URL != "https://api.kedaimaster.com" && (
<div className="Watermark"></div>
}
)}
<div style={{ width: isTablet ? "60%" : "100%" }}>
<div className="App-header">
<Header
HeaderText={"Menu"}
@@ -256,21 +323,23 @@ function CafePage({
isSpotifyNeedLogin={isSpotifyNeedLogin}
queue={queue}
setModal={setModal}
/>{
user.username !== undefined &&
/>
{user.username !== undefined &&
(user.cafeId === shopId || user.user_id === shopOwnerId) &&
(user.roleId === 1 || user.roleId === 2) && (
<div style={{
backgroundColor: '#5c7c5c',
padding: '7px 28px',
margin: '0 10px',
borderRadius: '15px',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
textShadow: '2px 2px 4px rgba(0, 0, 0, 0.7)',
fontSize: '16px'
}}>
<div
style={{
backgroundColor: "#5c7c5c",
padding: "7px 28px",
margin: "0 10px",
borderRadius: "15px",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
textShadow: "2px 2px 4px rgba(0, 0, 0, 0.7)",
fontSize: "16px",
}}
>
Mode Edit&nbsp;
<Switch
borderRadius={0}
@@ -278,8 +347,7 @@ function CafePage({
onChange={() => setIsEditMode(!isEditMode)}
/>
</div>
)
}
)}
<ItemTypeLister
user={user}
@@ -314,8 +382,12 @@ function CafePage({
setShopItems={setShopItems}
itemList={itemType.itemList}
typeVisibility={itemType.visibility}
moveItemTypeUp={(e) => moveItemTypeHandler(e, 'up', index)}
moveItemTypeDown={(e) => moveItemTypeHandler(e, 'down', index)}
moveItemTypeUp={(e) =>
moveItemTypeHandler(e, "up", index)
}
moveItemTypeDown={(e) =>
moveItemTypeHandler(e, "down", index)
}
isEditMode={isEditMode}
beingEditedType={beingEditedType}
setBeingEditedType={setBeingEditedType}
@@ -326,7 +398,7 @@ function CafePage({
price,
selectedImage,
description,
promoPrice,
promoPrice
) =>
createItem(
shopId,
@@ -335,41 +407,137 @@ function CafePage({
selectedImage,
itemTypeID,
description,
promoPrice,
promoPrice
)
}
handleUpdateItem={(itemId, name, price, selectedImage, description, promoPrice) =>
updateItem(itemId, name, price, selectedImage, description, promoPrice)
handleUpdateItem={(
itemId,
name,
price,
selectedImage,
description,
promoPrice
) =>
updateItem(
itemId,
name,
price,
selectedImage,
description,
promoPrice
)
}
/>
))}
{!isEditMode && (user.username || cartItemsLength > 0) &&
<div style={{ marginTop: '10px', height: '40px', position: 'sticky', bottom: '40px', display: 'flex', justifyContent: 'center', alignItems: 'center', textAlign: 'center' }}>
{(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' }}>{lastTransaction != null && '+'}{cartItemsLength} item</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', width: '130px' }}>
{((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' }}>
{!isEditMode && !isTablet && (user.username || cartItemsLength > 0) && (
<div
style={{
marginTop: "10px",
height: "40px",
position: "sticky",
bottom: "40px",
display: "flex",
justifyContent: "center",
alignItems: "center",
textAlign: "center",
}}
>
{(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",
}}
>
{lastTransaction != null && "+"}
{cartItemsLength} item
</div>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
width: "130px",
}}
>
{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>
</svg>
</div>
</div>
</div>
}
{user.username &&
<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' }}>
)}
{user.username && (
<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">
<g
transform="translate(0 460) scale(0.09 -0.09)"
style={{ fill: 'white', marginTop: '4px' }}
style={{ fill: "white", marginTop: "4px" }}
stroke="none"
>
<path
@@ -400,13 +568,39 @@ function CafePage({
</div>
</div>
</div>
}
)}
</div>
)}
</div>
</div>
{/* <Watermark /> */}
</div>
{isTablet &&
<div
style={{
width: "40%",
position: "fixed",
right: 0,
top: 0,
zIndex: 199
}}
>
<Cart
shopId={shopId}
shop={shop}
table={table}
setModal={setModal}
sendParam={sendParam}
socket={socket}
totalItemsCount={totalItemsCount}
deviceType={deviceType}
shopItems={shopItems}
setShopItems={setShopItems}
isTablet={true}
/>
</div>
}
</div>
</div>
<Watermark />
</div>
{/* )} */}
</>
);

View File

@@ -14,7 +14,7 @@ import {
handlePaymentFromGuestSide,
handlePaymentFromGuestDevice,
handleExtendFromGuestDevice,
handleCloseBillFromGuestDevice
handleCloseBillFromGuestDevice,
} from "../helpers/transactionHelpers";
import { getItemsByCafeId } from "../helpers/cartHelpers.js";
@@ -22,15 +22,22 @@ import { getItemsByCafeId } from "../helpers/cartHelpers.js";
import Dropdown from "./Dropdown.js";
import { useNavigationHelpers } from "../helpers/navigationHelpers";
export default function Invoice({ shopId, setModal, table, sendParam, deviceType, socket, shopItems, setShopItems }) {
export default function Invoice({
shopId,
setModal,
table,
sendParam,
deviceType,
socket,
shopItems,
setShopItems,
isTablet
}) {
const { shopIdentifier, tableCode } = useParams();
sendParam({ shopIdentifier, tableCode });
const {
goToShop
} = useNavigationHelpers(shopIdentifier, table.tableCode);
const { goToShop } = useNavigationHelpers(shopIdentifier, table.tableCode);
const [cartItems, setCartItems] = useState([]);
const [totalPrice, setTotalPrice] = useState(0);
@@ -51,14 +58,14 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
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) {
}
console.log(methods);
const lastTransaction = JSON.parse(
localStorage.getItem("lastTransaction")
);
if (lastTransaction?.payment_type == "paylater")
methods.isOpenBillAvailable = false;
setPaymentMethods(methods);
} catch (err) {}
};
if (shopId) {
@@ -70,29 +77,27 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
const fetchCartItems = async () => {
try {
const cart = getItemsByCafeId(shopId);
const itemMap = new Map(cart.map(item => [item.itemId, item.qty]));
const itemMap = new Map(cart.map((item) => [item.itemId, item.qty]));
// Step 2: Filter and transform shopItems
const filteredItems = shopItems
.map(itemType => ({
.map((itemType) => ({
itemTypeId: itemType.itemTypeId,
cafeId: itemType.cafeId,
typeName: itemType.name,
itemList: itemType.itemList
.filter(item => itemMap.has(item.itemId)) // Keep only items in getItemsByCafeId
.map(item => ({
.filter((item) => itemMap.has(item.itemId)) // Keep only items in getItemsByCafeId
.map((item) => ({
itemId: item.itemId,
price: (item.promoPrice ? item.promoPrice : item.price),
price: item.promoPrice ? item.promoPrice : item.price,
name: item.name,
image: item.image,
qty: itemMap.get(item.itemId), // Add qty from getItemsByCafeId
availability: item.availability
availability: item.availability,
})),
}))
}))
.filter(itemType => itemType.itemList.length > 0); // Remove empty itemTypes
console.log(filteredItems)
.filter((itemType) => itemType.itemList.length > 0); // Remove empty itemTypes
console.log(filteredItems);
// Update local storage by removing unavailable items
const updatedLocalStorage =
@@ -120,7 +125,10 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
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);
@@ -128,7 +136,7 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
setTimeout(function () {
setCartItems(filteredItems);
setTotalPrice(totalPrice);
setIsLoading(false)
setIsLoading(false);
}, 100); //delay is in milliseconds
} catch (error) {
console.error("Error fetching cart items:", error);
@@ -138,18 +146,17 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
fetchCartItems();
const getNewestCartItems = async () => {
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 => {
items.forEach((itemType) => {
itemType.itemList.forEach((item) => {
// Loop through the shopItems and find the corresponding itemId
shopItems.forEach(shopItemType => {
shopItemType.itemList.forEach(shopItem => {
shopItems.forEach((shopItemType) => {
shopItemType.itemList.forEach((shopItem) => {
if (shopItem.itemId === item.itemId) {
// Update shopItems with the new data from items (e.g., availability, price)
shopItem.availability = item.availability;
@@ -179,7 +186,8 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
}, 100); // delay is in milliseconds
// Update local storage by removing unavailable items and updating prices
const updatedLocalStorage = JSON.parse(localStorage.getItem("cart")) || [];
const updatedLocalStorage =
JSON.parse(localStorage.getItem("cart")) || [];
const newLocalStorage = updatedLocalStorage.map((cafe) => {
if (cafe.cafeId === shopId) {
return {
@@ -194,8 +202,10 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
// Update the price in the local storage item
return {
...item,
price: updatedItem.promoPrice ? updatedItem.promoPrice : updatedItem.price,
availability: updatedItem.availability
price: updatedItem.promoPrice
? updatedItem.promoPrice
: updatedItem.price,
availability: updatedItem.availability,
};
}
@@ -231,7 +241,10 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
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);
@@ -240,13 +253,12 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
console.error("Error fetching cart items:", error);
// Handle error if needed
}
};
getNewestCartItems();
}, [shopId]);
}, [shopId, localStorage.getItem('cart')]);
const handlePayCloseBill = async (orderMethod) =>{
const handlePayCloseBill = async (orderMethod) => {
setIsPaymentLoading(true);
console.log("tipe" + deviceType);
if (transactionData) {
@@ -257,7 +269,7 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
socketId
);
}
}
};
const handlePay = async (orderMethod) => {
setIsPaymentLoading(true);
@@ -271,13 +283,10 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
orderMethod,
socketId
);
localStorage.removeItem('lastTransaction')
localStorage.removeItem("lastTransaction");
// Dispatch the custom event
window.dispatchEvent(new Event("localStorageUpdated"));
}
else
if (deviceType == "clerk") {
} else if (deviceType == "clerk") {
const pay = await handlePaymentFromClerk(
shopId,
email,
@@ -331,26 +340,34 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
}, [table]);
useEffect(() => {
console.log(localStorage.getItem('cart'))
console.log(cartItems)
console.log(localStorage.getItem("cart"));
console.log(cartItems);
if (localStorage.getItem('cart') == null || localStorage.getItem('cart') == '' || 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'));
console.log(localStorageCart)
const localStorageCart = JSON.parse(localStorage.getItem("cart"));
console.log(localStorageCart);
// Create a set of itemIds from the local storage cart for quick lookup
const localStorageItemIds = new Set(localStorageCart[0].items.map(item => item.itemId));
const localStorageItemIds = new Set(
localStorageCart[0].items.map((item) => item.itemId)
);
// Filter out items from cartItems that do not exist in the local storage cart
const updatedCartItems = cartItems.map(itemType => ({
const updatedCartItems = cartItems.map((itemType) => ({
...itemType,
itemList: itemType.itemList.filter(item => localStorageItemIds.has(item.itemId))
itemList: itemType.itemList.filter((item) =>
localStorageItemIds.has(item.itemId)
),
}));
setCartItems(updatedCartItems);
const totalPrice = updatedCartItems.reduce((total, itemType) => {
return (
total +
@@ -360,8 +377,7 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
);
}, 0);
setTotalPrice(totalPrice);
}, [localStorage.getItem('cart')]);
}, [localStorage.getItem("cart")]);
const handleOrderTypeChange = (event) => {
setOrderType(event.target.value);
@@ -379,29 +395,66 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
setEmail(event.target.value);
};
const transactionData = JSON.parse(localStorage.getItem('lastTransaction'));
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>
{(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
viewBox="0 0 32 32"
style={{ fill: "#8F8787" }}
<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",
}}
>
{!isTablet &&
<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>
{transactionData == null && getItemsByCafeId(shopId).length < 1 ? (
<div
style={{
height: "75vh",
display: "flex",
justifyContent: "center",
flexDirection: "column",
alignContent: "center",
alignItems: "center",
}}
>
<div style={{ width: isTablet ? "30%":"50%" }}>
<svg viewBox="0 0 32 32" style={{ fill: "#8F8787" }}>
<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" />
</svg>
</div>
<h1 style={{ fontSize: '120%', color: '#8F8787' }}>Tidak ada item di keranjang</h1>
<h1 style={{ fontSize: "120%", color: "#8F8787" }}>
Tidak ada item di keranjang
</h1>
</div>
:
(isLoading ? <></> :
) : isLoading ? (
<></>
) : (
<>
{getItemsByCafeId(shopId).length > 0 &&
{getItemsByCafeId(shopId).length > 0 && (
<div className={styles.RoundedRectangle}>
{cartItems.map((itemType) => (
<ItemLister
@@ -448,14 +501,14 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
<span></span>
</div>
<div className={styles.NoteContainer} >
<div className={styles.NoteContainer}>
<input
className={styles.NoteInput}
placeholder="Tambahkan catatan..."
/>
</div>
<div className={styles.NoteContainer}style={{height: '18px'}}>
<div className={styles.NoteContainer} style={{ height: "18px" }}>
<span>Catatan :</span>
<span></span>
</div>
@@ -468,19 +521,40 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
/>
</div>
</div>
}
)}
{transactionData &&
<div className={styles.RoundedRectangle} style={{ backgroundColor: '#c3c3c3', fontSize: '15px', display: 'flex', justifyContent: 'space-between' }}>
{transactionData.payment_type != 'paylater' ?
{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}>
<div
onClick={() =>
setModal("transaction_item", {
transactionId: transactionData.transactionId,
})
}
className={styles.AddedLastTransaction}
>
Pembayaran akan ditambahkan ke transaksi sebelumnya
</div>
<div className={styles.CancelAddedLastTransaction} onClick={() => { window.location.reload(); localStorage.removeItem('lastTransaction') }}>
<div
className={styles.CancelAddedLastTransaction}
onClick={() => {
window.location.reload();
localStorage.removeItem("lastTransaction");
}}
>
<svg
style={{ width: '40px', height: '40px' }}
className={styles['plusNegative2']}
style={{ width: "40px", height: "40px" }}
className={styles["plusNegative2"]}
clipRule="evenodd"
fillRule="evenodd"
strokeLinejoin="round"
@@ -495,98 +569,141 @@ export default function Invoice({ shopId, setModal, table, sendParam, deviceType
</svg>
</div>
</>
:
) : (
<div className={styles.AddedLastTransaction}>
<div>
Open bill
<div onClick={() => setModal('transaction_item', { transactionId: transactionData.transactionId })}>
<div
onClick={() =>
setModal("transaction_item", {
transactionId: transactionData.transactionId,
})
}
>
Lihat tagihan
</div>
</div>
{getItemsByCafeId(shopId).length > 0 ?
<button className={styles.PayButton3} onClick={() => handlePay(orderMethod)}>
{getItemsByCafeId(shopId).length > 0 ? (
<button
className={styles.PayButton3}
onClick={() => handlePay(orderMethod)}
>
{isPaymentLoading ? (
<ColorRing height="50" width="50" color="white" />
<ColorRing height="40" width="40" color="white" />
) : (
<div>
{transactionData ?
{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}>
) : (
<button
className={styles.PayButton3}
style={{
backgroundColor: "rgb(42 145 24)",
letterSpacing: "1px",
}}
onClick={goToShop}
>
<div>
<span>Tambahkan item lain</span>
</div>
</button>}
</button>
)}
</div>
}
)}
</div>
}
)}
<div className={styles.PaymentOption}>
<div className={styles.TotalContainer}>
<span>Pembayaran</span>
<span>
{paymentMethods != null && <Dropdown setDropdownKey={() => setDropdownKey(dropdownKey + 1)} paymentMethods={paymentMethods} onChange={handleOrderMethodChange} />}
{paymentMethods != null && (
<Dropdown
setDropdownKey={() => setDropdownKey(dropdownKey + 1)}
paymentMethods={paymentMethods}
onChange={handleOrderMethodChange}
/>
)}
</span>
</div>
{transactionData && transactionData.payment_type === 'paylater' ?
<div style={{ display: 'flex', paddingLeft: '25px', paddingRight: '25px', marginTop: '17px' }}>
<button className={styles.PayButton} onClick={() => handlePayCloseBill(orderMethod)}>
{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{
transactionData.DetailedTransactions.reduce((total, transaction) => {
return total + (transaction.promoPrice == 0 || transaction.promoPrice == null
<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>
: 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)}>
) : (
<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 ?
{transactionData ? (
<span>Tambahkan</span>
:
) : (
<span>Pesan</span>
}
)}
<span>Rp{totalPrice}</span>
</div>
)}
</button>
</div>
}
)}
</div>
<div className={styles.PaymentOptionMargin}></div>
</>
)
}
)}
</div>
);
}

View File

@@ -1,3 +1,5 @@
@media (max-width: 768px) {
.Invoice {
overflow-x: hidden;
background-color: white;
@@ -40,11 +42,10 @@
font-size: calc(10px + 2vmin);
color: rgba(88, 55, 50, 1);
border-radius: 15px 15px 0 0;
position: fixed;
bottom: 0;
right: 0;
left: 0;
width: 100%;
}
.PaymentOptionMargin {
@@ -59,6 +60,10 @@
position: relative;
height: 220px;
bottom: 0;
right: 0;
width: 40%;
}
.TotalContainer {
@@ -92,13 +97,13 @@
margin-bottom: 23px;
}
.PayButton div{
.PayButton div {
display: flex;
justify-content: space-between;
padding-left: 20px;
padding-right: 20px;
}
.PayButton3{
.PayButton3 {
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 500;
font-style: normal;
@@ -115,8 +120,7 @@
margin-top: 10px;
}
.PayButton3 div{
.PayButton3 div {
display: flex;
justify-content: space-between;
padding-left: 20px;
@@ -201,22 +205,275 @@
overflow-wrap: break-word; /* Ensure text wraps */
}
.AddedLastTransaction{
.AddedLastTransaction {
width: 100%;
font-size: 1em;
padding: 10px 20px;
margin-bottom: 7px;
font-weight: 600;
}
.AddedLastTransaction div{
.AddedLastTransaction div {
display: flex;
justify-content: space-between;
}
.CancelAddedLastTransaction{
.CancelAddedLastTransaction {
width: 40px;
height: 40px;
margin-right: 30px;
margin-top: 10px;
transform: rotate(45deg);
}
}
@media (min-width: 768px) {
.Invoice {
overflow-x: hidden;
background-color: white;
display: flex;
flex-direction: column;
justify-content: flex-start;
font-size: calc(10px + 2vmin);
color: rgba(88, 55, 50, 1);
background-color: #e9e9e9;
}
.TotalContainer {
display: flex;
justify-content: space-between;
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 600;
font-style: normal;
font-size: 1em;
padding: 10px 30px;
padding-top: 20px;
/* margin-bottom: 17px; */
}
.Invoice-title {
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 6vw;
color: rgba(88, 55, 50, 1);
text-align: left;
margin-left: 20px;
margin-top: 7px;
}
.Invoice-detail {
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 20px;
color: rgba(88, 55, 50, 1);
text-align: left;
margin-left: 20px;
margin-top: 17px;
}
.PaymentOption {
overflow: visible;
background-color: white;
display: flex;
flex-direction: column;
justify-content: center;
font-size: calc(10px + 2vmin);
color: rgba(88, 55, 50, 1);
border-radius: 15px 15px 0 0;
position: fixed;
bottom: 0;
right: 0;
width: 40%;
left: 60%;
}
.PaymentOptionMargin {
z-index: -1;
overflow-x: hidden;
background-color: white;
display: flex;
flex-direction: column;
justify-content: center;
font-size: calc(10px + 2vmin);
color: rgba(88, 55, 50, 1);
position: relative;
height: 220px;
bottom: 0;
right: 0;
width: 40%;
}
.PayButton {
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 20px;
width: 80vw;
height: 40px;
border-radius: 50px;
background-color: rgba(88, 55, 50, 1);
color: white;
border: none;
margin: 0px auto;
cursor: pointer;
margin-bottom: 23px;
}
.PayButton div {
display: flex;
justify-content: space-between;
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);
font-size: 1em;
margin-bottom: 25px;
cursor: pointer;
}
.Confirm {
display: flex;
justify-content: space-between;
width: 80vw;
margin: 0 auto;
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 600;
font-style: normal;
font-size: 1.5em;
padding: 10px 0;
margin-bottom: 17px;
}
.RoundedRectangle {
border-radius: 20px;
padding-top: 5px;
margin: 12px;
background-color: #f9f9f9;
}
.EmailContainer {
display: flex;
justify-content: space-between;
width: 80vw;
margin: 0 auto;
font-size: 1em;
padding: 10px 0;
margin-bottom: 7px;
}
.OrderTypeContainer {
display: flex;
justify-content: space-between;
width: 80vw;
margin: 0 auto;
font-size: 1em;
padding: 10px 0;
margin-bottom: 7px;
}
.Note {
text-align: left;
color: rgba(88, 55, 50, 1);
font-size: 1em;
margin-bottom: 13px;
margin-left: 50px;
cursor: pointer;
}
.NoteContainer {
display: flex;
justify-content: space-between;
width: 80vw;
margin: 0 auto;
font-size: 1em;
padding: 10px 0;
margin-bottom: 7px;
font-weight: 600;
}
.NoteInput {
width: 78vw;
height: 18px;
border-radius: 20px;
margin: 0 auto;
padding: 10px;
font-size: 15px;
border: 1px solid rgba(88, 55, 50, 0.5);
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);
}
.PaymentOptionMargin {
z-index: -1;
overflow-x: hidden;
background-color: white;
display: flex;
flex-direction: column;
justify-content: center;
font-size: calc(10px + 2vmin);
color: rgba(88, 55, 50, 1);
position: relative;
height: 220px;
}
.TotalContainer {
display: flex;
justify-content: space-between;
width: 80vw;
margin: 0 auto;
font-family: "Plus Jakarta Sans", sans-serif;
font-weight: 600;
font-style: normal;
font-size: 1.3em;
padding: 10px 0;
padding-top: 20px;
/* margin-bottom: 17px; */
}
}

View File

@@ -42,8 +42,12 @@
line-height: 2.25rem;
letter-spacing: -1px;
margin-bottom: 6px;
margin: 10px
}
.buttonWrapper {
display: flex;
}
.descHeading {
width: 99%;
font-weight: 700;

View File

@@ -27,10 +27,12 @@ const LinktreePage = ({ handleYes, handleNo }) => {
<div className={styles.dashboardContainer} >
<div className={styles.mainHeading}>{captMessage}</div>
{descMessage && <div className={styles.descHeading}>{descMessage}</div>}
{handleYes && <div onClick={handleYes} className={styles.button}>{yesText}</div>}
<div className={styles.buttonWrapper}>
<div onClick={handleYes} className={styles.button}>{yesText}</div>
{noText && <div onClick={handleNo} className={styles.button}>{noText}</div>}
</div>
</div>
</div>
);
};

View File

@@ -164,15 +164,15 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
{isPaymentLoading ? (
<ColorRing height="50" width="50" color="white" />
) : transaction.confirmed === 1 ? (
"Confirm has paid" // Display "Confirm has paid" if the transaction is confirmed (1)
"Konfirmasi telah bayar" // Display "Confirm has paid" if the transaction is confirmed (1)
) : transaction.confirmed === -1 ? (
"Declined" // Display "Declined" if the transaction is declined (-1)
"Ditolah" // Display "Declined" if the transaction is declined (-1)
) : transaction.confirmed === 2 ? (
"Confirm item has ready" // Display "Item ready" if the transaction is ready (2)
"Konfirmasi item telah siap" // Display "Item ready" if the transaction is ready (2)
) : transaction.confirmed === 3 ? (
"Transaction success" // Display "Item ready" if the transaction is ready (2)
"Transaksi selesai" // Display "Item ready" if the transaction is ready (2)
) : (
"Confirm availability" // Display "Confirm availability" if the transaction is not confirmed (0)
"Konfirmasi ketersediaan" // Display "Confirm availability" if the transaction is not confirmed (0)
)}
</button>
</div>
@@ -181,7 +181,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
className={styles.DeclineButton}
onClick={() => handleDecline(transaction.transactionId)}
>
decline
Tolak
</h5>
)}
</div>

View File

@@ -16,12 +16,8 @@ export default function Transaction_pending() {
<div className={styles.Transaction}>
<div className={containerStyle}>
<div style={{ marginTop: "30px", textAlign: "center" }}>
<h2>transaction failed</h2>
<img
className={styles.expression}
src="https://i.imgur.com/B6k9exa.png"
alt="Failed"
/>
<h2>Transaksi dibatalkan</h2>
</div>
</div>
</div>