latest update 27 jul 24

This commit is contained in:
zadit
2024-07-27 10:58:43 +07:00
commit 4f43b46e5f
66 changed files with 24005 additions and 0 deletions

156
src/pages/CafePage.js Normal file
View File

@@ -0,0 +1,156 @@
// src/CafePage.js
import React, { useState, useEffect } from "react";
import { useParams, useSearchParams, useNavigate } from "react-router-dom";
import "../App.css";
import SearchInput from "../components/SearchInput";
import ItemTypeLister from "../components/ItemTypeLister";
import { MusicPlayer } from "../components/MusicPlayer";
import ItemLister from "../components/ItemLister";
import AccountUpdateModal from "../components/AccountUpdateModal";
import Header from "../components/Header";
import { ThreeDots } from "react-loader-spinner";
import { getItemTypesWithItems } from "../helpers/itemHelper.js";
import {
getLocalStorage,
updateLocalStorage,
} from "../helpers/localStorageHelpers";
function CafePage({
sendParam,
socket,
user,
guestSides,
guestSideOfClerk,
removeConnectedGuestSides,
}) {
const [searchParams] = useSearchParams();
const token = searchParams.get("token");
const { shopId } = useParams();
sendParam(shopId);
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [screenMessage, setScreenMessage] = useState("");
const [shopItems, setShopItems] = useState([]);
const [isSpotifyNeedLogin, setNeedSpotifyLogin] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
useEffect(() => {
if (user.cafeId != null && user.cafeId != shopId) {
navigate("/" + user.cafeId);
sendParam(user.cafeId);
}
if (user.password == "unsetunsetunset") setIsModalOpen(true);
}, [user]);
useEffect(() => {
if (token) {
updateLocalStorage("auth", token);
}
}, [token]);
const handleModalClose = () => {
setIsModalOpen(false);
};
const handleLogout = () => {
updateLocalStorage("auth", "");
navigate(0);
};
useEffect(() => {
async function fetchData() {
try {
setLoading(true);
const { response, data } = await getItemTypesWithItems(shopId);
console.log(data);
if (response.status === 200) {
setShopItems(data);
setLoading(false);
socket.emit("join-room", { token: getLocalStorage("auth"), shopId });
socket.on("joined-room", (response) => {
const { isSpotifyNeedLogin } = response;
setNeedSpotifyLogin(isSpotifyNeedLogin);
});
socket.on("transaction_created", () => {
console.log("transaction created");
});
} else {
setScreenMessage("Kafe tidak tersedia");
}
} catch (error) {
console.error("Error fetching shop items:", error);
setLoading(false); // Ensure loading state is turned off on error
}
}
fetchData();
}, [shopId]);
if (loading)
return (
<div className="Loader">
<div className="LoaderChild">
<ThreeDots />
<h1>{screenMessage}</h1>
</div>
</div>
);
else
return (
<div className="App">
<body className="App-header">
<Header
HeaderText={"Menu"}
isEdit={() => setIsModalOpen(true)}
isLogout={handleLogout}
shopId={shopId}
user={user}
guestSides={guestSides}
guestSideOfClerk={guestSideOfClerk}
removeConnectedGuestSides={removeConnectedGuestSides}
/>
<div style={{ marginTop: "5px" }}></div>
<SearchInput />
<div style={{ marginTop: "15px" }}></div>
<ItemTypeLister user={user} shopId={shopId} itemTypes={shopItems} />
<div style={{ marginTop: "-13px" }}></div>
<h2 className="title">Music Req.</h2>
<MusicPlayer
socket={socket}
shopId={shopId}
user={user}
isSpotifyNeedLogin={isSpotifyNeedLogin}
/>
<div style={{ marginTop: "-15px" }}></div>
{shopItems.map((itemType) => (
<ItemLister
shopId={shopId}
user={user}
key={itemType.itemTypeId}
itemTypeId={itemType.itemTypeId}
typeName={itemType.name}
itemList={itemType.itemList}
/>
))}
</body>
{user.username && (
<AccountUpdateModal
user={user}
isOpen={isModalOpen}
onClose={handleModalClose}
/>
)}
</div>
);
}
export default CafePage;

213
src/pages/Cart.js Normal file
View File

@@ -0,0 +1,213 @@
import React, { useRef, useEffect, useState } from 'react';
import styles from './Cart.module.css';
import ItemLister from '../components/ItemLister';
import { ThreeDots, ColorRing } from 'react-loader-spinner';
import { useParams } from 'react-router-dom';
import { useNavigationHelpers } from '../helpers/navigationHelpers';
import { getTable } from '../helpers/tableHelper.js';
import { getCartDetails } from '../helpers/itemHelper.js';
import { getItemsByCafeId } from '../helpers/cartHelpers'; // Import getItemsByCafeId
import Modal from '../components/Modal'; // Import the reusable Modal component
export default function Cart({ sendParam, totalItemsCount }) {
const { shopId } = useParams();
sendParam(shopId);
const { goToShop, goToInvoice } = useNavigationHelpers(shopId);
const [cartItems, setCartItems] = useState([]);
const [totalPrice, setTotalPrice] = useState(0);
const [orderType, setOrderType] = useState('pickup');
const [tableNumber, setTableNumber] = useState('');
const [loading, setLoading] = useState(true);
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalContent, setModalContent] = useState(null);
const [isCheckoutLoading, setIsCheckoutLoading] = useState(false); // State for checkout button loading animation
const [email, setEmail] = useState('');
const textareaRef = useRef(null);
useEffect(() => {
const fetchCartItems = async () => {
try {
setLoading(true);
const items = await getCartDetails(shopId);
setLoading(false);
if (items) setCartItems(items);
const initialTotalPrice = items.reduce((total, itemType) => {
return total + itemType.itemList.reduce((subtotal, item) => {
return subtotal + (item.qty * item.price);
}, 0);
}, 0);
setTotalPrice(initialTotalPrice);
} catch (error) {
console.error('Error fetching cart items:', error);
}
};
fetchCartItems();
const textarea = textareaRef.current;
if (textarea) {
const handleResize = () => {
textarea.style.height = 'auto';
textarea.style.height = `${textarea.scrollHeight}px`;
};
textarea.addEventListener('input', handleResize);
handleResize();
return () => textarea.removeEventListener('input', handleResize);
}
}, [shopId]);
const refreshTotal = async () => {
try {
const items = await getItemsByCafeId(shopId);
const updatedTotalPrice = items.reduce((total, localItem) => {
const cartItem = cartItems.find(itemType =>
itemType.itemList.some(item => item.itemId === localItem.itemId)
);
if (cartItem) {
const itemDetails = cartItem.itemList.find(item => item.itemId === localItem.itemId);
return total + (localItem.qty * itemDetails.price);
}
return total;
}, 0);
setTotalPrice(updatedTotalPrice);
} catch (error) {
console.error('Error refreshing total price:', error);
}
};
const handleOrderTypeChange = (event) => {
setOrderType(event.target.value);
};
const handleTableNumberChange = (event) => {
setTableNumber(event.target.value);
};
const handleEmailChange = (event) => {
setEmail(event.target.value);
};
const handlCloseModal = () => {
setIsModalOpen(false);
setIsCheckoutLoading(false);
}
const isValidEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
const handleCheckout = async () => {
setIsCheckoutLoading(true); // Start loading animation
if (email != '' && !isValidEmail(email)) {
setModalContent(<div>Please enter a valid email address.</div>);
setIsModalOpen(true);
setIsCheckoutLoading(false); // Stop loading animation
return;
}
if (orderType === 'serve') {
if (tableNumber !== '') {
const table = await getTable(shopId, tableNumber);
if (!table) {
setModalContent(<div>Table not found. Please enter a valid table number.</div>);
setIsModalOpen(true);
} else {
goToInvoice(orderType, tableNumber, email);
}
} else {
setModalContent(<div>Please enter a table number.</div>);
setIsModalOpen(true);
}
} else {
goToInvoice(orderType, tableNumber, email);
}
setIsCheckoutLoading(false); // Stop loading animation
};
if (loading)
return (
<div className='Loader'>
<div className='LoaderChild'>
<ThreeDots />
</div>
</div>
);
else
return (
<div className={styles.Cart}>
<div style={{ marginTop: '30px' }}></div>
<h2 className={styles['Cart-title']}>{totalItemsCount} {totalItemsCount !== 1 ? 'items' : 'item'} in Cart</h2>
<div style={{ marginTop: '-45px' }}></div>
{cartItems.map(itemType => (
<ItemLister
key={itemType.itemTypeId}
refreshTotal={refreshTotal}
shopId={shopId}
forCart={true}
typeName={itemType.typeName}
itemList={itemType.itemList}
/>
))}
<div className={styles.EmailContainer}>
<label htmlFor="email">Email:</label>
<input
type="email"
id="email"
value={email}
onChange={handleEmailChange}
className={styles.EmailInput}
/>
</div>
<div className={styles.OrderTypeContainer}>
<span htmlFor="orderType">Order Type:</span>
<select id="orderType" value={orderType} onChange={handleOrderTypeChange}>
<option value="pickup">Pickup</option>
<option value="serve">Serve</option>
</select>
{orderType === 'serve' && (
<input
type="text"
placeholder="Table Number"
value={tableNumber}
onChange={handleTableNumberChange}
className={styles.TableNumberInput}
/>
)}
</div>
<div className={styles.NoteContainer}>
<span>Note</span>
<span></span>
</div>
<textarea
ref={textareaRef}
className={styles.NoteInput}
placeholder="Add a note..."
/>
<div className={styles.TotalContainer}>
<span>Total:</span>
<span>Rp {totalPrice}</span>
</div>
<button onClick={handleCheckout} className={styles.CheckoutButton}>
{isCheckoutLoading ? <ColorRing height="50" width="50" color="white" /> : 'Checkout'}
</button>
<div onClick={goToShop} className={styles.BackToMenu}>Back to menu</div>
<Modal isOpen={isModalOpen} onClose={() => handlCloseModal()}>
{modalContent}
</Modal>
</div>
);
}

106
src/pages/Cart.module.css Normal file
View File

@@ -0,0 +1,106 @@
.Cart {
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);
}
.Cart-title {
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 32px;
color: rgba(88, 55, 50, 1);
text-align: left;
margin-left: 20px;
margin-top: 17px;
}
.CheckoutContainer{
bottom: 0px;
position: fixed;
}
.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;
}
.NoteInput {
width: 78vw;
height: 12vw;
border-radius: 20px;
margin: 0 auto;
padding: 10px;
font-size: 1.2em;
border: 1px solid rgba(88, 55, 50, 0.5);
margin-bottom: 27px;
resize: none; /* Prevent resizing */
overflow-wrap: break-word; /* Ensure text wraps */
}
.TotalContainer {
display: flex;
justify-content: space-between;
width: 80vw;
margin: 0 auto;
font-size: 1.5em;
padding: 10px 0;
margin-bottom: 17px;
}
.CheckoutButton {
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 32px;
width: 80vw;
height: 18vw;
border-radius: 50px;
background-color: rgba(88, 55, 50, 1);
color: white;
border: none;
margin: 0px auto;
cursor: pointer;
margin-bottom: 23px;
display: flex;
align-items: center;
justify-content: center;
}
.BackToMenu {
text-align: center;
color: rgba(88, 55, 50, 1);
font-size: 1em;
margin-bottom: 25px;
cursor: pointer;
}

93
src/pages/Dashboard.js Normal file
View File

@@ -0,0 +1,93 @@
import React, { useState, useEffect } from "react";
import styles from "./Dashboard.module.css"; // Import module CSS for styling
import Header from "../components/Header";
import { useNavigate } from "react-router-dom";
import AccountUpdateModal from "../components/AccountUpdateModal";
import { updateLocalStorage } from "../helpers/localStorageHelpers";
import { getAllCafeOwner } from "../helpers/userHelpers";
import { getOwnedCafes } from "../helpers/cafeHelpers";
import { ThreeDots } from "react-loader-spinner";
const Dashboard = ({ user }) => {
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [isModalOpen, setIsModalOpen] = useState(false);
const [items, setItems] = useState([]);
useEffect(() => {
if (user && user.roleId === 0) {
setLoading(true);
// Example of calling getAllCafeOwner if roleId is 0
getAllCafeOwner()
.then((data) => {
setItems(data); // Assuming getAllCafeOwners returns an array of cafe owners
setLoading(false);
})
.catch((error) => {
console.error("Error fetching cafe owners:", error);
});
}
if (user && user.roleId === 1) {
// Example of calling getAllCafeOwner if roleId is 0
setLoading(true);
getOwnedCafes(user.userId)
.then((data) => {
setItems(data); // Assuming getAllCafeOwners returns an array of cafe owners
setLoading(false);
})
.catch((error) => {
console.error("Error fetching cafe owners:", error);
});
}
}, [user]);
const handleModalClose = () => {
setIsModalOpen(false);
};
const handleLogout = () => {
updateLocalStorage("auth", "");
navigate(0);
};
return (
<>
<Header
HeaderText={"GrooveBrew"}
isEdit={() => setIsModalOpen(true)}
isLogout={handleLogout}
user={user}
/>
{user && user.roleId < 2 && (
<div className={styles.dashboard}>
{loading && <ThreeDots />}
{items.map((item, index) => (
<div
key={index}
onClick={() => navigate("/" + item.cafeId)}
className={styles.rectangle}
>
{item.name || item.username}
</div>
))}
{user && user.roleId < 1 ? (
<div className={styles.rectangle}>Create Admin</div>
) : (
<div className={styles.rectangle}>Create Cafe</div>
)}
</div>
)}
{user.username && (
<AccountUpdateModal
user={user}
isOpen={isModalOpen}
onClose={handleModalClose}
/>
)}
</>
);
};
export default Dashboard;

View File

@@ -0,0 +1,29 @@
.dashboard {
display: grid;
grid-template-columns: repeat(
auto-fill,
minmax(200px, 1fr)
); /* Responsive grid */
gap: 20px; /* Gap between rectangles */
padding: 10px;
}
.rectangle {
height: 150px; /* Height of each rectangle */
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px; /* Rounded corners */
font-size: 24px;
background-color: rgb(114, 114, 114);
}
/* Media query for mobile devices */
@media (max-width: 600px) {
.dashboard {
grid-template-columns: repeat(
auto-fill,
minmax(100%, 1fr)
); /* Single column for mobile */
}
}

63
src/pages/GuestSide.js Normal file
View File

@@ -0,0 +1,63 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import QRCode from "qrcode.react";
import { Oval } from "react-loader-spinner";
import styles from "./GuestSide.module.css"; // Import the CSS Module
import { updateLocalStorage } from "../helpers/localStorageHelpers";
const GuestSide = ({ socket }) => {
const navigate = useNavigate();
const [qrCode, setQrCode] = useState("");
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
socket.emit("req_guestSide");
socket.on("res_guest_side", (data) => {
setLoading(false);
setQrCode(data);
console.log(data);
});
socket.on("qrCode_hasRead", (response) => {
const { authGuestCode, shopId } = response;
updateLocalStorage("authGuestSide", authGuestCode);
updateLocalStorage("auth", "");
navigate("/" + shopId, { replace: true, state: { refresh: true } });
});
} catch (error) {
console.error("Error fetching shop items:", error);
setLoading(false); // Ensure loading state is turned off on error
}
};
fetchData();
// Clean up on component unmount
return () => {
socket.off("res_guest_side");
socket.off("qrCode_hasRead");
};
}, [socket]);
return (
<div className={styles.container}>
{loading ? (
<div className={styles.loading}>
<Oval height="80" width="80" color="grey" ariaLabel="loading" />
</div>
) : (
<>
<QRCode value={qrCode} size={256} />
<h1>{qrCode}</h1>
</>
)}
</div>
);
};
export default GuestSide;

View File

@@ -0,0 +1,14 @@
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: white;
color: black;
}
.loading {
font-size: 24px;
font-weight: bold;
}

View File

@@ -0,0 +1,84 @@
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { QrReader } from "react-qr-reader"; // Import QrReader as named import
import styles from "./GuestSideLogin.module.css"; // Import module CSS file for styles
import { getLocalStorage } from "../helpers/localStorageHelpers";
const GuestSideLogin = ({ socket }) => {
const navigate = useNavigate();
const [qrCode, setQRCode] = useState(""); // State to store QR code
socket.on("qrCode_readSuccess", (response) => {
const { shopId } = response;
console.log("qr has been read");
navigate("/" + shopId);
});
const setLoginGuestSide = () => {
const token = getLocalStorage("auth");
socket.emit("read_qrCode", { qrCode, token });
};
// Function to handle QR code scan
const handleScan = (data) => {
if (data) {
setQRCode(data.text); // Set scanned QR code to state
setLoginGuestSide(); // Send QR code to backend
}
};
// Function to handle QR scan error
const handleError = (err) => {
console.error(err);
};
// Function to handle manual input
const handleManualInput = (e) => {
setQRCode(e.target.value);
};
useEffect(() => {
if (qrCode.length === 11) {
const timer = setTimeout(() => {
setLoginGuestSide();
}, 1000); // Delay of 1 second (1000 milliseconds)
return () => clearTimeout(timer); // Cleanup the timer if qrCode changes before the delay completes
}
}, [qrCode]);
return (
<div className={styles.qrisReaderContainer}>
<div className={styles.qrScannerContainer}>
<QrReader
constraints={{ facingMode: "environment" }}
delay={500}
onResult={handleScan}
onError={handleError}
videoId="video"
className={styles.qrReader} // Apply the class
videoContainerStyle={{
width: "100vw",
height: "100vh",
paddingTop: "0px",
}}
videoStyle={{ width: "100%", height: "100%" }}
/>
<div className={styles.focusSquare}></div>
</div>
<div className={styles.inputContainer}>
{/* Manual input form */}
<input
type="text"
value={qrCode}
onChange={handleManualInput}
placeholder="Enter QRIS Code"
/>
</div>
</div>
);
};
export default GuestSideLogin;

View File

@@ -0,0 +1,73 @@
.qrisReaderContainer {
display: flex;
flex-direction: column;
align-items: center;
height: 100vh;
width: 100vw;
background-color: #f0f0f0; /* Example background color */
overflow: hidden;
}
.qrReader {
transform: scaleX(-1); /* Flip horizontally */
}
.qrScannerContainer {
position: relative;
width: 100%; /* Full width */
}
.qrScannerContainer section {
position: relative;
overflow: hidden;
}
.qrScannerContainer video {
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover; /* Ensure video fills the entire container */
display: block;
position: absolute;
transform: scaleX(-1); /* Flip video horizontally */
}
.focusSquare {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50vw; /* Adjust width as needed */
height: 50vw; /* Adjust height as needed */
border: 2px solid rgb(75, 75, 75); /* Example border for visibility */
}
.inputContainer {
position: absolute;
width: 300px;
height: 40px;
padding: 10px;
bottom: 50px;
background-color: white;
border-radius: 20px;
display: flex;
align-items: center;
}
.inputContainer input {
flex: 1;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc; /* Example border */
border-radius: 4px; /* Example border radius */
margin-right: 10px; /* Example margin */
}
.inputContainer button {
padding: 10px 20px;
font-size: 16px;
background-color: #007bff; /* Example button color */
color: #fff; /* Example text color */
border: none;
border-radius: 4px; /* Example border radius */
cursor: pointer;
}

136
src/pages/Invoice.js Normal file
View File

@@ -0,0 +1,136 @@
import React, { useEffect, useState } from "react";
import styles from "./Invoice.module.css";
import { useParams, useLocation } from "react-router-dom"; // Changed from useSearchParams to useLocation
import { ThreeDots, ColorRing } from "react-loader-spinner";
import ItemLister from "../components/ItemLister";
import { getCartDetails } from "../helpers/itemHelper";
import {
handlePaymentFromClerk,
handlePaymentFromGuestSide,
handlePaymentFromGuestDevice,
} from "../helpers/transactionHelpers";
export default function Invoice({ sendParam, deviceType }) {
const { shopId } = useParams();
const location = useLocation(); // Use useLocation hook instead of useSearchParams
const searchParams = new URLSearchParams(location.search); // Pass location.search directly
const email = searchParams.get("email");
const orderType = searchParams.get("orderType");
const tableNumber = searchParams.get("tableNumber");
useEffect(() => {
sendParam(shopId);
}, [sendParam, shopId]);
const [cartItems, setCartItems] = useState([]);
const [totalPrice, setTotalPrice] = useState(0);
const [isPaymentLoading, setIsPaymentLoading] = useState(false); // State for payment button loading animation
useEffect(() => {
const fetchCartItems = async () => {
try {
const items = await getCartDetails(shopId);
setCartItems(items);
// Calculate total price based on fetched cart items
const totalPrice = items.reduce((total, itemType) => {
return (
total +
itemType.itemList.reduce((subtotal, item) => {
return subtotal + item.qty * item.price;
}, 0)
);
}, 0);
setTotalPrice(totalPrice);
} catch (error) {
console.error("Error fetching cart items:", error);
// Handle error if needed
}
};
fetchCartItems();
}, [shopId]);
const handlePay = async (isCash) => {
setIsPaymentLoading(true);
console.log("tipe" + deviceType);
if (deviceType == "clerk") {
const pay = await handlePaymentFromClerk(
shopId,
email,
isCash ? "cash" : "cashless",
orderType,
tableNumber,
);
} else if (deviceType == "guestDevice") {
const pay = await handlePaymentFromGuestSide(
shopId,
email,
isCash ? "cash" : "cashless",
orderType,
tableNumber,
);
} else if (deviceType == "guestDevice") {
const pay = await handlePaymentFromGuestDevice(
shopId,
isCash ? "cash" : "cashless",
orderType,
tableNumber,
);
}
console.log("transaction from " + deviceType + "success");
setIsPaymentLoading(false);
};
return (
<div className={styles.Invoice}>
<div style={{ marginTop: "30px" }}></div>
<h2 className={styles["Invoice-title"]}>Invoice</h2>
<div style={{ marginTop: "30px" }}></div>
<div className={styles.RoundedRectangle}>
{cartItems.map((itemType) => (
<ItemLister
shopId={shopId}
forInvoice={true}
key={itemType.id}
typeName={itemType.typeName}
itemList={itemType.itemList}
/>
))}
<h2 className={styles["Invoice-detail"]}>
{orderType === "pickup"
? "Diambil di kasir"
: `Diantar ke meja nomor ${tableNumber}`}
</h2>
<div className={styles.TotalContainer}>
<span>Total:</span>
<span>Rp {totalPrice}</span>
</div>
</div>
<div className={styles.PaymentOption}>
<div className={styles.TotalContainer}>
<span>Payment Option</span>
<span></span>
</div>
<button className={styles.PayButton} onClick={() => handlePay(false)}>
{isPaymentLoading ? (
<ColorRing height="50" width="50" color="white" />
) : (
"Cashless"
)}
</button>
<div className={styles.Pay2Button} onClick={() => handlePay(true)}>
{isPaymentLoading ? (
<ColorRing height="12" width="12" color="white" />
) : (
"Cash"
)}
</div>
</div>
<div className={styles.PaymentOptionMargin}></div>
</div>
);
}

View File

@@ -0,0 +1,107 @@
.Invoice {
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);
background-color: #e9e9e9;
}
.Invoice-title {
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 32px;
color: rgba(88, 55, 50, 1);
text-align: left;
margin-left: 20px;
margin-top: 17px;
}
.Invoice-detail {
font-family: "Poppins", 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-x: hidden;
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: 75px;
right: 0;
left: 0;
}
.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: 229.39px;
}
.TotalContainer {
display: flex;
justify-content: space-between;
width: 80vw;
margin: 0 auto;
font-family: "Poppins", sans-serif;
font-weight: 600;
font-style: normal;
font-size: 1.5em;
padding: 10px 0;
margin-bottom: 17px;
}
.PayButton {
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 32px;
width: 80vw;
height: 18vw;
border-radius: 50px;
background-color: rgba(88, 55, 50, 1);
color: white;
border: none;
margin: 0px auto;
cursor: pointer;
margin-bottom: 23px;
}
.Pay2Button {
text-align: center;
color: rgba(88, 55, 50, 1);
font-size: 1em;
margin-bottom: 25px;
cursor: pointer;
}
.RoundedRectangle {
border-radius: 20px;
padding-top: 5px;
margin: 26px;
background-color: #f9f9f9;
}

57
src/pages/LoginPage.css Normal file
View File

@@ -0,0 +1,57 @@
/* src/Login.css */
.login-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #f0f0f0;
}
.login-form {
background-color: #ffffff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
}
.login-form h2 {
margin-bottom: 20px;
text-align: center;
color: #333333;
}
.input-group {
margin-bottom: 15px;
}
.input-group label {
display: block;
margin-bottom: 5px;
color: #555555;
}
.input-group input {
width: 100%;
padding: 10px;
border: 1px solid #cccccc;
border-radius: 4px;
box-sizing: border-box;
}
.login-button {
width: 100%;
padding: 10px;
background-color: #007bff;
color: #ffffff;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.login-button:hover {
background-color: #0056b3;
}

42
src/pages/LoginPage.js Normal file
View File

@@ -0,0 +1,42 @@
import React from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import './LoginPage.css';
import RouletteWheel from '../components/RouletteWheel';
import { loginUser } from '../helpers/userHelpers'; // Import from userHelper.js
const LoginPage = () => {
const navigate = useNavigate();
const location = useLocation(); // Use useLocation hook instead of useSearchParams
const searchParams = new URLSearchParams(location.search); // Pass location.search directly
const next = searchParams.get('next');
console.log(next);
const handleLogin = async (email, username, password) => {
try {
const response = await loginUser(username, password);
if (response.success) {
localStorage.setItem("auth", response.token);
if (response.cafeId !== null) {
navigate(`/${response.cafeId}`);
} else {
if (next) navigate(`/${next}`);
else navigate('/');
}
} else {
console.error('Login failed');
}
} catch (error) {
console.error('Error occurred while logging in:', error.message);
}
};
return (
<div className="login-container">
<RouletteWheel onSign={handleLogin} />
</div>
);
};
export default LoginPage;