This commit is contained in:
zadit
2024-12-26 08:58:59 +07:00
parent c6d7ed5aae
commit cf38edac85
17 changed files with 259 additions and 167 deletions

View File

@@ -204,11 +204,6 @@ function App() {
setModal("transaction_failed", data);
});
socket.on("transaction_canceled", async (data) => {
console.log("transaction notification");
setModal("transaction_canceled", data);
});
const checkNotifications = () => {
let permission = Notification.permission;
@@ -280,24 +275,34 @@ function App() {
socket.off("signout-guest-session");
};
}, [socket, shopId]);
async function checkIfStillViewingOtherTransaction(){
console.log("transaction notification");
console.log(modalContent);
let response;
response = await getTransactionsFromCafe(shopId, 0, true);
transactionList.current = response;
console.log(response);
// 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
console.log(transaction_info); // Log the updated transaction_info
// If transaction_info is an empty string, set the modal
if (transaction_info == '') return false;
else return true;
}
useEffect(() => {
// This will ensure that searchParams and transaction_info get updated on each render
socket.on("transaction_created", async (data) => {
console.log("transaction notification");
console.log(modalContent);
let response;
response = await getTransactionsFromCafe(shopId, 0, true);
console.log(data);
transactionList.current = response;
// 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
console.log(transaction_info); // Log the updated transaction_info
const isViewingOtherTransaction = await checkIfStillViewingOtherTransaction();
// If transaction_info is an empty string, set the modal
if (transaction_info === '') {
if (!isViewingOtherTransaction) {
setModal("new_transaction", data);
}
@@ -312,9 +317,20 @@ function App() {
});
});
socket.on("transaction_canceled", async (data) => {
console.log("transaction notification");
const isViewingOtherTransaction = await checkIfStillViewingOtherTransaction();
// If transaction_info is an empty string, set the modal
if (!isViewingOtherTransaction) {
setModal("new_transaction", data);
}
});
// Clean up the socket event listener on unmount or when dependencies change
return () => {
socket.off("transaction_created");
socket.off("transaction_canceled");
};
}, [socket, shopId, location]); // Ensure location is in the dependencies to respond to changes in the URL

View File

@@ -190,28 +190,32 @@ const ChildWrapper = styled.div`
flex-direction: column;
width: 100%;
`;
const Child = styled.div`
width: 100%;
height: 40px;
margin: 5px;
background-color: rgba(88, 55, 50, 0.2);
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
padding-top: 10px;
padding-left: 5px;
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
${(props) =>
props.hasChildren &&
`
height: auto;
padding-bottom: 10px;
`}
props.hasChildren
? `
border: 1px solid #ababab;
height: auto;
padding-bottom: 10px;
`
: `
display: flex;
align-items: center;
background-color: rgb(223 223 223);
`}
`;
const Header = ({
HeaderText,
HeaderSize='6vw',
@@ -343,7 +347,7 @@ const Header = ({
{shopName}
<Child>
Mode edit
Mode edit &nbsp;
<Switch
borderRadius={0}
checked={isEditMode}

View File

@@ -144,6 +144,7 @@
margin-left: 5px;
color: #d9c61c;
text-align: right;
margin-top: 22px;
}
.itemQty {

View File

@@ -90,7 +90,7 @@
background-color: #0000008c;
width: 100%;
top: 7px;
bottom: -10px;
bottom: -4px;
display: flex;
align-items: center;
justify-content: center;

View File

@@ -16,19 +16,22 @@
top: 0;
left: 0;
right: 0;
height: 142px;
height: 120px;
/* Adjust height as needed */
background-size: cover;
/* Adjust background image size */
background-position: center;
/* Center the background image */
filter: blur(1.5px);
-webkit-filter: blur(1.5px);
border-radius: 13px 13px 0 0;
/* filter: blur(1.5px);
-webkit-filter: blur(1.5px); */
/* border-radius: 13px 13px 0 0; */
background-color: rgb(95 121 89);
/* Rounded corners at the top */
text-align: right;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
border-radius: 0 0 15px 15px;
z-index: 1;
}
.current-name {
@@ -73,6 +76,8 @@
font-size: 18px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
/* Text shadow for readability */
margin-bottom: -47px;
}
.progress-container {
@@ -124,6 +129,17 @@
/* Allow vertical scrolling */
}
.expandable-container > div:first-child {
padding-top: 21px;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
*/ }
.expand-button {
font-size: 20px;
position: relative;
@@ -138,7 +154,21 @@
text-align: center;
line-height: 40px;
/* Center text vertically */
}
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
outline: none;
transition: padding-top 0.3s ease;
padding-top: 8px;
}
.expand-button h5 {
font-weight: 500;
@@ -146,8 +176,8 @@
text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.36);
}
.expand-button:hover {
background-color: #73a585;
.expand-button.expanded{
padding-top: 0px;
}
/* Adjust height of the music player container when expanded */
@@ -224,10 +254,6 @@
/* Adjust fill color */
}
/* Add hover effect for the search icon */
.search-box .search-icon:hover {
color: #555;
}
.rectangle {
position: relative;
height: 200px;

View File

@@ -284,16 +284,16 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
const handleSetPlayer = () => {
const token = localStorage.getItem("auth");
socket.emit("claimPlayer", {
token,
shopId,
});
if (isSpotifyNeedLogin) {
socket.emit("claimPlayer", {
token,
shopId,
});
} else {
socket.emit("unClaimPlayer", {
token,
shopId,
});
// socket.emit("unClaimPlayer", {
// token,
// shopId,
// });
}
};
@@ -333,9 +333,9 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
}, [expanded]);
const [text, setText] = useState("Awaiting the next hit");
const [text, setText] = useState("Menunggu musik favoritmu");
const textIndex = useRef(0);
const [messages, setMessages] = useState(["Awaiting the next hit", "Click to request your fav song"]);
const [messages, setMessages] = useState(["Menunggu musik favoritmu", "Klik untuk putar musik favoritmu"]);
useEffect(() => {
@@ -343,8 +343,8 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
const newMessages = [
currentSong != null && currentSong.item != undefined
? `${currentSong.item.artists[0].name} - ${currentSong.item.name}`
: "Awaiting the next hit",
"Click to request your fav song"
: "Menunggu musik favoritmu",
"Klik untuk putar musik favoritmu"
];
setMessages(newMessages);
@@ -374,9 +374,10 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
<div
onClick={toggleView}
className="current-bgr"
style={{ backgroundImage: `url(${videoSrc != "" ? '' : backgroundImage})` }}
style={{ backgroundImage: `url(${backgroundImage})` }}
// style={{ backgroundImage: `url(${videoSrc != "" ? '' : backgroundImage})` }}
>
<video
{/* <video
ref={videoRef}
autoPlay
loop
@@ -385,7 +386,7 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
style={{ height: '100%', width: '100%', objectFit: 'cover', position: 'absolute', top: 0, right: 0, zIndex: -1 }}
>
{videoSrc && <source src={videoSrc} type="video/mp4" />}
</video>
</video> */}
{currentLines.present.map((line, index) => (
<div className="present" style={{
color: subtitleColor,
@@ -419,7 +420,7 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
currentSong.item.album.images[0] &&
currentSong.item.artists[0].name
? currentSong.item.artists[0].name
: "Drop your hits below"}
: "Pilih hits terbaikmu dibawah!"}
</div>
<div className="progress-container">
<div
@@ -457,13 +458,13 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
<input
type="text"
placeholder={
isSpotifyNeedLogin ? "Set as music player" : "Unset as music player"
isSpotifyNeedLogin ? "Jadikan perangkat sebagai pemutar musik" : "Unset as music player"
}
onClick={handleSetPlayer}
/>
</div>
)}
<div className="search-box">
<div className="search-box" style={{}}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
@@ -473,7 +474,7 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
</svg>
<input
type="text"
placeholder="Search..."
placeholder="cari..."
value={songName}
onChange={handleInputChange}
/>
@@ -490,24 +491,24 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
/>
))}
{
songName === "" &&
queue &&
Array.isArray(queue) &&
queue.length > 0 && (
queue.map((song, index) => (
<MusicComponent
key={index}
song={song}
min={-100}
max={100}
onDecision={(vote) => onDecision(song.trackId, vote)}
/>
))
)
}
songName === "" &&
queue &&
Array.isArray(queue) &&
queue.length > 0 && (
queue.map((song, index) => (
<MusicComponent
key={index}
song={song}
min={-100}
max={100}
onDecision={(vote) => onDecision(song.trackId, vote)}
/>
))
)
}
{songName == "" && queue.length < 1 && (
<div className="rectangle">
<div className="diagonal-text">No Beats Ahead - Drop Your Hits</div>
<div className="diagonal-text">Antrian kosong - Pilih musikmu</div>
</div>
)}
{songName == "" && queue.length > 0 && queue.length < 3 && (
@@ -516,11 +517,10 @@ export function MusicPlayer({ socket, shopId, user, shopOwnerId, isSpotifyNeedLo
</div>
)}
</div>
<div className="expand-button" onClick={toggleExpand}>
<div className={`expand-button ${expanded ? "expanded" : ""}`} onClick={toggleExpand}>
<h5>
{expanded
? "︿"
: "request your song"}
{expanded? '⋀' : 'Lihat antrian musik'}
</h5>
</div></>
}

View File

@@ -141,7 +141,7 @@ const SetPaymentQr = ({ shopId }) => {
</button>
</div>
<div style={styles.switchContainer}>
<h1>Pengecekan ketersediaan ganda</h1>
<h3>Pengecekan ketersediaan ganda</h3>
<p style={styles.description}>
Nyalakan agar kasir memeriksa kembali ketersediaan produk sebelum pelanggan membayar.
</p>

View File

@@ -144,9 +144,11 @@ export default function Invoice({ table, sendParam, deviceType, socket }) {
console.log(localStorage.getItem('cart'))
console.log(cartItems)
if(localStorage.getItem('cart') == "[]") return;
// Parse the local storage cart
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));

View File

@@ -1,9 +1,8 @@
import React, { useState } from 'react';
import { createClerks } from '../helpers/userHelpers'; // Adjust the import path as needed
import { createCafe } from '../helpers/cafeHelpers'; // Adjust the import path as needed
const CreateClerk = ({ shopId }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState('');
@@ -13,16 +12,16 @@ const CreateClerk = ({ shopId }) => {
setMessage('');
// Basic validation
if (!username || !password) {
setMessage('Username and password are required');
if (!name) {
setMessage('name is required');
setLoading(false);
return;
}
try {
const create = await createClerks(shopId, username, password);
const create = await createCafe(name);
if (create) setMessage('Clerk created successfully');
if (create) setMessage('Cafe created successfully');
else setMessage('Failed to create clerk');
} catch (error) {
setMessage('Error creating clerk');
@@ -37,16 +36,9 @@ const CreateClerk = ({ shopId }) => {
<form onSubmit={handleSubmit} style={styles.form}>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
style={styles.input}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Cafe name"
value={name}
onChange={(e) => setName(e.target.value)}
style={styles.input}
/>
<button type="submit" style={styles.button} disabled={loading}>

View File

@@ -1,7 +1,8 @@
import React, { useState } from 'react';
import { createClerks } from '../helpers/userHelpers'; // Adjust the import path as needed
import { createCafeOwner } from '../helpers/userHelpers'; // Adjust the import path as needed
const CreateClerk = ({ shopId }) => {
const CreateClerk = () => {
const [email, setEmail] = useState('');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [loading, setLoading] = useState(false);
@@ -20,7 +21,7 @@ const CreateClerk = ({ shopId }) => {
}
try {
const create = await createClerks(shopId, username, password);
const create = await createCafeOwner(email, username, password);
if (create) setMessage('Clerk created successfully');
else setMessage('Failed to create clerk');
@@ -33,8 +34,15 @@ const CreateClerk = ({ shopId }) => {
return (
<div style={styles.container}>
<h2 style={styles.header}>Tambah Kedai</h2>
<h2 style={styles.header}>Tambah Tenant</h2>
<form onSubmit={handleSubmit} style={styles.form}>
<input
type="text"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
style={styles.input}
/>
<input
type="text"
placeholder="Username"
@@ -50,7 +58,7 @@ const CreateClerk = ({ shopId }) => {
style={styles.input}
/>
<button type="submit" style={styles.button} disabled={loading}>
{loading ? 'Creating...' : 'Create Clerk'}
{loading ? 'Creating...' : 'Create Tenant'}
</button>
{message && (
<p style={{ ...styles.message, color: message.includes('success') ? 'green' : 'red' }}>

View File

@@ -497,7 +497,7 @@
.cafeListWrapper {
background-color: white;
border-radius: 20px 20px 0 0;
top: 83vh;
bottom: 0;
position: absolute;
width: 100%;
}

View File

@@ -138,7 +138,7 @@ export default function Transactions({
<div className={styles['receipt-header']}>
<ColorRing className={styles['receipt-logo']} />
<div className={styles['receipt-info']}>
<h3>silahkan bayar ke kasir</h3>
{transaction.payment_type == 'cashless' ? <h3>silahkan bayar QR</h3> : <h3>silahkan bayar ke kasir</h3>}
<p>Transaction ID: {transaction.transactionId}</p>
<p>Payment Type: {transaction.payment_type}</p>
</div>

View File

@@ -5,7 +5,7 @@ import { ColorRing } from "react-loader-spinner";
import {
getTransaction,
confirmTransaction,
declineTransaction,
cancelTransaction,
} from "../helpers/transactionHelpers";
import { getTables } from "../helpers/tableHelper";
import TableCanvas from "../components/TableCanvas";
@@ -29,12 +29,24 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
try {
const fetchedTransaction = await getTransaction(transactionId);
setTransaction(fetchedTransaction);
console.log(transaction);
console.log(fetchedTransaction); // Log the fetched transaction
} catch (error) {
console.error("Error fetching transaction:", error);
}
};
fetchData();
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(() => {
@@ -75,7 +87,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
if (isPaymentLoading) return;
setIsPaymentLoading(true);
try {
const c = await declineTransaction(transactionId);
const c = await cancelTransaction(transactionId);
// if (c) {
// // Update the confirmed status locally
// setTransactions((prevTransactions) =>
@@ -180,7 +192,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType, handl
<div className={styles.TotalContainer}>
<button
className={styles.PayButton}
onClick={() => handleConfirm(transaction.transactionId)}
onClick={() => handleDecline(transaction.transactionId)}
disabled={
transaction.confirmed === -1 ||
transaction.confirmed === 3 ||

View File

@@ -25,9 +25,9 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
const fetchTransactions = async () => {
try {
let response;
response = await getTransactionsFromCafe(shopId || propsShopId, 5, false);
setTransactions(response);
response = await getMyTransactions(shopId || propsShopId, 5);
response = await getTransactionsFromCafe(shopId || propsShopId, 5, false);
setTransactions(response);
response = await getMyTransactions(shopId || propsShopId, 5);
setMyTransactions(response);
} catch (error) {
console.error("Error fetching transactions:", error);
@@ -101,10 +101,10 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
return (
<div className={styles.Transactions}>
<div style={{ marginTop: "30px" }}></div>
<h2 className={styles["Transactions-title"]}>Transactions</h2>
<h2 className={styles["Transactions-title"]}>Daftar transaksi</h2>
<div style={{ marginTop: "30px" }}></div>
{/* <TableCanvas tables={tables} selectedTable={selectedTable} /> */}
<div className={styles.TransactionListContainer}>
<div className={styles.TransactionListContainer} style={{ padding: '0 20px 0 20px' }}>
{transactions &&
transactions.map((transaction) => (
<div
@@ -114,37 +114,66 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
setSelectedTable(transaction.Table || { tableId: 0 })
}
>
<div className={styles['receipt-header']}>
<div className={styles['receipt-info']}>
<h3>{transaction.confirmed === 1 ? (
"Silahkan cek pembayaran"
) : transaction.confirmed === -1 ? (
"Declined"
) : transaction.confirmed === -2 ? (
"Canceled"
) : transaction.confirmed === 2 ? (
"Sedang diproses"
) : transaction.confirmed === 3 ? (
"Transaction success"
) : (
"Silahkan cek ketersediaan"
)}</h3>
<p>Transaction ID: {transaction.transactionId}</p>
<p>Payment Type: {transaction.payment_type}</p>
</div>
</div>
<div className={styles['dotted-line']}>
<div className={styles['circle-left']}>
<div className={styles['receipt-header']}>
{transaction.confirmed === 1 ? (
<ColorRing className={styles['receipt-logo']} />
) : transaction.confirmed === -1 || transaction.confirmed === -2 ? (
<div style={{ display: 'flex', justifyContent: 'center' }}>
<svg
style={{ width: '60px', transform: 'Rotate(45deg)' }}
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['line']} ></div>
<div className={styles['circle-right']} >
) : transaction.confirmed === 2 ? (
<ColorRing className={styles['receipt-logo']} />
) : transaction.confirmed === 3 ? (
"Transaction success"
) : (
<ColorRing className={styles['receipt-logo']} />
)}
<div className={styles['receipt-info']}>
<h3>{transaction.confirmed === 1 ? (
"Silahkan cek pembayaran"
) : transaction.confirmed === -1 ? (
"Dibatalkan oleh kasir"
) : transaction.confirmed === -2 ? (
"Dibatalkan oleh pelanggan"
) : transaction.confirmed === 2 ? (
"Sedang diproses"
) : transaction.confirmed === 3 ? (
"Transaction success"
) : (
"Silahkan cek ketersediaan"
)}</h3>
<p>Transaction ID: {transaction.transactionId}</p>
<p>Payment Type: {transaction.payment_type}</p>
</div>
</div>
</div>
<div className={styles['dotted-line']}>
<div className={styles['circle-left']}>
</div>
<div className={styles['line']} ></div>
<div className={styles['circle-right']} >
</div>
</div>
{transaction.paymentClaimed && transaction.confirmed < 2 && (
<div className={styles.RibbonBanner}>
<img src={"https://i.imgur.com/yt6osgL.png"}></img>
@@ -162,27 +191,26 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
<h2 className={styles["Transactions-detail"]}>
{transaction.serving_type === "pickup"
? "Self pickup"
: `Serve to ${
transaction.Table ? transaction.Table.tableNo : "N/A"
}`}
: `Serve to ${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}
disabled
/>
</div>
</>
)}
{transaction.notes != "" && (
<>
<div className={styles.NoteContainer}>
<span>Note :</span>
<span></span>
</div>
<div className={styles.NoteContainer}>
<textarea
className={styles.NoteInput}
value={transaction.notes}
disabled
/>
</div>
</>
)}
<div className={styles.TotalContainer}>
<span>Total:</span>
<span>

View File

@@ -8,7 +8,7 @@
color: rgba(88, 55, 50, 1);
background-color: #e9e9e9;
border-radius: 15px;
max-height: 80vh;
max-height: 87vh;
width: 80vw;
}
.Transactions {
@@ -226,7 +226,7 @@
.dotted-line .line {
border-top: 13px dotted #dbdbdb;
width: 100%;
margin: 0 20px;
margin: 0 18px;
}
.dotted-line .circle-left {