This commit is contained in:
zadit
2024-12-04 21:40:36 +07:00
parent 529a7e505c
commit 198d0b3053
9 changed files with 517 additions and 109 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useRef } from "react";
import "./App.css";
import "./components/Loading.css";
import {
@@ -26,6 +26,9 @@ import GuestSide from "./pages/GuestSide";
import { getItemTypesWithItems } from "./helpers/itemHelper.js";
import { getTableByCode } from "./helpers/tableHelper.js";
import {
getTransactionsFromCafe,
} from "./helpers/transactionHelpers";
import {
getConnectedGuestSides,
getClerks,
@@ -57,8 +60,21 @@ function App() {
const [shopItems, setShopItems] = useState([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalContent, setModalContent] = useState(null);
const transactionList = useRef(null);
const [queue, setQueue] = useState([]);
const validTransactionStates = [
'new_transaction',
'transaction_canceled',
'transaction_pending',
'transaction_confirmed',
'payment_claimed',
'transaction_success',
'transaction_end',
'transaction_failed',
];
useEffect(() => {
const calculateTotalsFromLocalStorage = () => {
const { totalCount } = calculateTotals(shopId);
@@ -193,20 +209,6 @@ function App() {
setModal("transaction_canceled", data);
});
//for clerk
socket.on("transaction_created", async (data) => {
console.log("transaction notification");
setModal("new_transaction", data);
let permission = Notification.permission;
if (permission != "granted") return;
navigator.serviceWorker.ready.then((registration) => {
registration.showNotification("New Transaction", {
body: `A new transaction was created: ${data.transactionDetails}`, // Customize as needed
icon: "icon.png", // Optional icon
});
});
});
const checkNotifications = () => {
let permission = Notification.permission;
@@ -266,8 +268,8 @@ function App() {
});
socket.on("updateQueue", (response) => {
setQueue(response); // Only set the queue if it's a valid non-empty array
console.log("Updated Queue:", response); // Log the valid queue
setQueue(response); // Only set the queue if it's a valid non-empty array
console.log("Updated Queue:", response); // Log the valid queue
});
@@ -275,6 +277,76 @@ function App() {
socket.off("signout-guest-session");
};
}, [socket, shopId]);
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
// If transaction_info is an empty string, set the modal
if (transaction_info === '') {
setModal("new_transaction", data);
}
// Show browser notification
let permission = Notification.permission;
if (permission !== "granted") return;
navigator.serviceWorker.ready.then((registration) => {
registration.showNotification("New Transaction", {
body: `A new transaction was created: ${data.transactionDetails}`,
icon: "icon.png", // Optional icon
});
});
});
// Clean up the socket event listener on unmount or when dependencies change
return () => {
socket.off("transaction_created");
};
}, [socket, shopId, location]); // Ensure location is in the dependencies to respond to changes in the URL
function handleMoveToTransaction(direction, from) {
console.log(direction);
console.log(from);
// Find the current index based on the 'from' transactionId
const currentIndex = transactionList.current.findIndex(item => item.transactionId == from);
// Determine the new transactionId based on the direction
let newTransactionId;
if (direction === 'next') {
// If we're not at the last transaction, get the next transactionId
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') {
// If we're not at the first transaction, get the previous transactionId
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);
// Update the URL with the new transactionId using navigate
navigate(`?transactionId=${newTransactionId}`, { replace: true });
// Optionally, update state or perform further actions based on the new transactionId
// Example:
// setModalContent({ cafeId: shopId, transactionId: newTransactionId });
}
const handleModalFromURL = () => {
const queryParams = new URLSearchParams(location.search);
@@ -307,7 +379,7 @@ function App() {
document.body.style.overflow = "hidden";
setIsModalOpen(true);
setModalContent(content);
setModalContent(content)
};
// Function to close the modal
@@ -318,6 +390,7 @@ function App() {
closeTheseContent.includes(modalContent))
) {
setIsModalOpen(false);
setModalContent(null);
document.body.style.overflow = "auto";
const queryParams = new URLSearchParams(location.search);
@@ -448,7 +521,7 @@ function App() {
guestSideOfClerk={guestSideOfClerk}
removeConnectedGuestSides={rmConnectedGuestSides}
setModal={setModal} // Pass the function to open modal
loading={shop.name==null}
loading={shop.name == null}
queue={queue}
/>
<Footer
@@ -551,6 +624,8 @@ function App() {
shop={shop}
isOpen={isModalOpen}
modalContent={modalContent}
handleMoveToTransaction={handleMoveToTransaction}
welcomePageConfig={shop.welcomePageConfig}
onClose={closeModal}
setModal={setModal}
/>

View File

@@ -1,4 +1,4 @@
import React from "react";
import React, {useState, useEffect} from "react";
import styles from "./Modal.module.css";
import CreateClerk from "../pages/CreateClerk"
import CreateCafe from "../pages/CreateCafe"
@@ -23,7 +23,19 @@ import GuidePage from "../pages/GuidePage";
import Join from "../pages/Join";
import Login from "../pages/Login";
import ResetPassword from "../pages/ResetPassword";
const Modal = ({ shop, isOpen, onClose, modalContent, setModal }) => {
import { getImageUrl } from "../helpers/itemHelper.js";
const Modal = ({ shop, isOpen, onClose, modalContent, setModal, handleMoveToTransaction,welcomePageConfig }) => {
const [shopImg, setShopImg] = useState('');
useEffect(() => {
if (welcomePageConfig) {
const parsedConfig = JSON.parse(welcomePageConfig);
setShopImg( getImageUrl(parsedConfig.image) || "")
}
}, [welcomePageConfig]);
if (!isOpen) return null;
// Function to handle clicks on the overlay
@@ -49,7 +61,7 @@ const Modal = ({ shop, isOpen, onClose, modalContent, setModal }) => {
{modalContent === "create_tenant" && <CreateTenant shopId={shop.cafeId} />}
{modalContent === "edit_tables" && <TablesPage shop={shop} />}
{modalContent === "new_transaction" && (
<Transaction propsShopId={shop.cafeId} />
<Transaction propsShopId={shop.cafeId} handleMoveToTransaction={handleMoveToTransaction} shopImg={shopImg} />
)}
{modalContent === "transaction_canceled" && (
<Transaction propsShopId={shop.cafeId} />

View File

@@ -12,9 +12,7 @@
}
.modalContent {
width: 90%;
/* height: 80%; */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 80%;
position: relative;
overflow: visible; /* Add this line to enable scrolling */
}

View File

@@ -170,11 +170,11 @@ export async function getMyTransactions() {
console.error("Error:", error);
}
}
export async function getTransactionsFromCafe(shopId, demand) {
export async function getTransactionsFromCafe(shopId, demand, idsOnly) {
try {
const token = getLocalStorage("auth");
const response = await fetch(
`${API_BASE_URL}/transaction/get-transactions-from-cafe/${shopId}?demandLength=${demand}`,
`${API_BASE_URL}/transaction/get-transactions/${shopId}?demandLength=${demand}&&idsOnly=${idsOnly}`,
{
method: "GET",
headers: {

View File

@@ -11,7 +11,7 @@ import { getTables } from "../helpers/tableHelper";
import TableCanvas from "../components/TableCanvas";
import { useSearchParams } from "react-router-dom";
export default function Transactions({ propsShopId, sendParam, deviceType }) {
export default function Transactions({ propsShopId, sendParam, deviceType, handleMoveToTransaction, shopImg }) {
const { shopId, tableId } = useParams();
if (sendParam) sendParam({ shopId, tableId });
@@ -108,9 +108,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
return (
<div className={styles.Transactions}>
<div style={{ marginTop: "30px" }}></div>
<h2 className={styles["Transactions-title"]}>Transactions</h2>
{/* <TableCanvas tables={tables} selectedTable={selectedTable} /> */}
<div className={styles.TransactionListContainer}>
{transaction && (
<div
@@ -120,13 +118,59 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
setSelectedTable(transaction.Table || { tableId: 0 })
}
>
<div className={styles['receipt-header']}>
<img
src={shopImg} // Placeholder image URL
alt='logo'
className={styles['receipt-logo']}
/>
<div className={styles['receipt-info']}>
<h3>Receipt Information</h3>
<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']} onClick={() => handleMoveToTransaction('previous', transaction.transactionId)}>
<div className={styles["inner-circle"]}>
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
fill="#000000"
style={{ transform: 'Rotate(-90deg)', width: '100%', height: '100%' }} // Ensure SVG fits the div
>
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path d="M 0.198 14.001 C -0.192 14.392 -0.088 11.275 0.1 11.088 L 7.293 4.293 C 7.684 3.902 8.316 3.902 8.707 4.293 L 15.833 11.258 C 16.021 11.445 16.203 14.644 15.812 14.253 L 8 6.414 L 0.198 14.001 Z" fill="#dbdbdb"></path>
</g>
</svg>
</div>
<div className={styles.circle}>1</div>
</div>
<div className={styles['line']} ></div>
<div className={styles['circle-right']} onClick={() => handleMoveToTransaction('next', transaction.transactionId)}>
<div className={styles["inner-circle"]}>
<span className="inner-text"><svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
fill="#000000"
style={{ transform: 'Rotate(90deg)', width: '100%', height: '100%' }} // Ensure SVG fits the div
>
<g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<path d="M 0.198 14.001 C -0.192 14.392 -0.088 11.275 0.1 11.088 L 7.293 4.293 C 7.684 3.902 8.316 3.902 8.707 4.293 L 15.833 11.258 C 16.021 11.445 16.203 14.644 15.812 14.253 L 8 6.414 L 0.198 14.001 Z" fill="#dbdbdb"></path>
</g>
</svg>
</span>
</div>
</div>
</div>
<div className={styles.RibbonBanner}></div>
<h2 className={styles["Transactions-detail"]}>
Transaction ID: {transaction.transactionId}
</h2>
<h2 className={styles["Transactions-detail"]}>
Payment Type: {transaction.payment_type}
</h2>
<ul>
{transaction.DetailedTransactions.map((detail) => (
<li key={detail.detailedTransactionId}>
@@ -137,10 +181,9 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
</ul>
<h2 className={styles["Transactions-detail"]}>
{transaction.serving_type === "pickup"
? "Self pickup"
: `Serve to ${
transaction.Table ? transaction.Table.tableNo : "N/A"
}`}
? "Ambil sendiri"
: `Diantar ke meja ${transaction.Table ? transaction.Table.tableNo : "N/A"
}`}
</h2>
{transaction.notes != "" && (
<>
@@ -178,17 +221,17 @@ 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)
"Ditolak" // Display "Declined" if the transaction is declined (-1)
) : transaction.confirmed === -2 ? (
"Canceled" // Display "Declined" if the transaction is declined (-1)
"Dibatalkan" // 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 pesanan siap" // Display "Item ready" if the transaction is ready (2)
) : transaction.confirmed === 3 ? (
"Transaction success" // Display "Item ready" if the transaction is ready (2)
) : (
"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>
@@ -197,7 +240,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
className={styles.DeclineButton}
onClick={() => handleDecline(transaction.transactionId)}
>
decline
batalkan
</h5>
)}
</div>

View File

@@ -124,9 +124,6 @@ export default function Transactions({
return (
<div className={styles.Transactions}>
<div style={{ marginTop: "30px" }}></div>
<h2 className={styles["Transactions-title"]}>Transactions</h2>
{/* <TableCanvas tables={tables} selectedTable={selectedTable} /> */}
<div className={styles.TransactionListContainer}>
{transaction && (
<div
@@ -137,6 +134,24 @@ export default function Transactions({
}
style={{ overflow: "hidden" }}
>
<div className={styles['receipt-header']}>
<ColorRing className={styles['receipt-logo']} />
<div className={styles['receipt-info']}>
<h3>silahkan bayar ke kasir</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>
<div className={styles['line']} ></div>
<div className={styles['circle-right']} >
</div>
</div>
<h2 className={styles["Transactions-detail"]}>
Transaction ID: {transaction.transactionId}
</h2>
@@ -209,8 +224,7 @@ export default function Transactions({
>
{transaction.payment_type == 'cash' || isPaymentLoading ? (
<>
{transaction.payment_type == 'cash' && <p>tunggu konfirmasi</p>}
<ColorRing height="50" width="50" color="white" />
{transaction.payment_type == 'cash' && <p>Bayar nanti</p>}
</>
) : isPaymentOpen ? (
"Claim has paid" // Display "Confirm has paid" if the transaction is confirmed (1)

View File

@@ -1,35 +1,207 @@
import React from "react";
import { ColorRing } from "react-loader-spinner";
import React, { useRef, useEffect, useState } from "react";
import styles from "./Transactions.module.css";
import { useParams } from "react-router-dom";
import { ColorRing } from "react-loader-spinner";
import {
getTransaction,
confirmTransaction,
declineTransaction,
} from "../helpers/transactionHelpers";
import { getTables } from "../helpers/tableHelper";
import TableCanvas from "../components/TableCanvas";
import { useSearchParams } from "react-router-dom";
export default function Transaction_pending() {
const containerStyle = {
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100%", // This makes the container stretch to the bottom of the viewport
backgroundColor: "#000", // Optional: Set a background color if you want to see the color ring clearly
export default function Transactions({ propsShopId, sendParam, deviceType, handleMoveToTransaction, shopImg }) {
const { shopId, tableId } = useParams();
if (sendParam) sendParam({ shopId, tableId });
const [tables, setTables] = useState([]);
const [selectedTable, setSelectedTable] = useState(null);
const [isPaymentLoading, setIsPaymentLoading] = useState(false);
const [searchParams] = useSearchParams();
const [transaction, setTransaction] = useState(null);
const noteRef = useRef(null);
useEffect(() => {
const transactionId = searchParams.get("transactionId") || "";
const fetchData = async () => {
try {
const fetchedTransaction = await getTransaction(transactionId);
setTransaction(fetchedTransaction);
console.log(transaction);
} catch (error) {
console.error("Error fetching transaction:", error);
}
};
fetchData();
}, [searchParams]);
useEffect(() => {
const fetchData = async () => {
try {
const fetchedTables = await getTables(shopId || propsShopId);
setTables(fetchedTables);
} catch (error) {
console.error("Error fetching tables:", error);
}
};
fetchData();
}, [shopId || propsShopId]);
const calculateTotalPrice = (detailedTransactions) => {
return detailedTransactions.reduce((total, dt) => {
return total + dt.qty * dt.Item.price;
}, 0);
};
const handleConfirm = async (transactionId) => {
if (isPaymentLoading) return;
setIsPaymentLoading(true);
try {
const c = await confirmTransaction(transactionId);
if (c) {
setTransaction({ ...transaction, confirmed: c.confirmed });
}
} catch (error) {
console.error("Error processing payment:", error);
} finally {
setIsPaymentLoading(false);
}
};
const handleDecline = async (transactionId) => {
if (isPaymentLoading) return;
setIsPaymentLoading(true);
try {
const c = await declineTransaction(transactionId);
// if (c) {
// // Update the confirmed status locally
// setTransactions((prevTransactions) =>
// prevTransactions.map((transaction) =>
// transaction.transactionId === transactionId
// ? { ...transaction, confirmed: -1 } // Set to confirmed
// : transaction
// )
// );
// }
} catch (error) {
console.error("Error processing payment:", error);
} finally {
setIsPaymentLoading(false);
}
};
const autoResizeTextArea = (textarea) => {
if (textarea) {
textarea.style.height = "auto"; // Reset height
textarea.style.height = `${textarea.scrollHeight}px`; // Set new height
}
};
useEffect(() => {
if (noteRef.current) {
autoResizeTextArea(noteRef.current);
}
}, [transaction?.notes]);
return (
<div className={styles.Transactions}>
<div className={containerStyle}>
<div style={{ marginTop: "30px", textAlign: "center" }}>
<h2>Transation Pending</h2>
<div style={{ marginTop: "20px" }}>
<ColorRing
visible={true}
height="80"
width="80"
ariaLabel="blocks-loading"
wrapperStyle={{}}
wrapperClass="blocks-wrapper"
colors={["#4fa94d", "#f7c34c", "#ffa53c", "#e34f53", "#d23a8d"]}
/>
<p>Waiting for clerk confirmation...</p>
<div className={styles.TransactionListContainer}>
{transaction && (
<div
key={transaction.transactionId}
className={styles.RoundedRectangle}
onClick={() =>
setSelectedTable(transaction.Table || { tableId: 0 })
}
>
<div className={styles['receipt-header']}>
<ColorRing className={styles['receipt-logo']} />
<div className={styles['receipt-info']}>
<h3>sedang memeriksa 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']} onClick={() => handleMoveToTransaction('previous', transaction.transactionId)}>
</div>
<div className={styles['line']} ></div>
<div className={styles['circle-right']} onClick={() => handleMoveToTransaction('next', transaction.transactionId)}>
</div>
</div>
<div className={styles.RibbonBanner}></div>
<ul>
{transaction.DetailedTransactions.map((detail) => (
<li key={detail.detailedTransactionId}>
<span>{detail.Item.name}</span> - {detail.qty} x Rp{" "}
{detail.Item.price}
</li>
))}
</ul>
<h2 className={styles["Transactions-detail"]}>
{transaction.serving_type === "pickup"
? "Ambil sendiri"
: `Diantar ke meja ${transaction.Table ? transaction.Table.tableNo : "N/A"
}`}
</h2>
{transaction.notes != "" && (
<>
<div className={styles.NoteContainer}>
<span>Note :</span>
<span></span>
</div>
<div className={styles.NoteContainer}>
<textarea
className={styles.NoteInput}
value={transaction.notes}
ref={noteRef}
disabled
/>
</div>
</>
)}
<div className={styles.TotalContainer}>
<span>Total:</span>
<span>
Rp {calculateTotalPrice(transaction.DetailedTransactions)}
</span>
</div>
<div className={styles.TotalContainer}>
<button
className={styles.PayButton}
onClick={() => handleConfirm(transaction.transactionId)}
disabled={
transaction.confirmed === -1 ||
transaction.confirmed === 3 ||
isPaymentLoading
} // Disable button if confirmed (1) or declined (-1) or
>
{isPaymentLoading ? (
<ColorRing height="50" width="50" color="white" />
) : transaction.confirmed === 1 ? (
"Konfirmasi telah bayar" // Display "Confirm has paid" if the transaction is confirmed (1)
) : transaction.confirmed === -1 ? (
"Ditolak" // Display "Declined" if the transaction is declined (-1)
) : transaction.confirmed === -2 ? (
"Dibatalkan" // Display "Declined" if the transaction is declined (-1)
) : transaction.confirmed === 2 ? (
"Konfirmasi pesanan siap" // Display "Item ready" if the transaction is ready (2)
) : transaction.confirmed === 3 ? (
"Transaction success" // Display "Item ready" if the transaction is ready (2)
) : (
"Batalkan" // Display "Confirm availability" if the transaction is not confirmed (0)
)}
</button>
</div>
</div>
</div>
)}
</div>
</div>
);

View File

@@ -25,7 +25,7 @@ export default function Transactions({ propsShopId, sendParam, deviceType }) {
const fetchTransactions = async () => {
try {
let response;
response = await getTransactionsFromCafe(shopId || propsShopId, 5);
response = await getTransactionsFromCafe(shopId || propsShopId, 5, false);
setTransactions(response);
response = await getMyTransactions(shopId || propsShopId, 5);
setMyTransactions(response);

View File

@@ -1,6 +1,5 @@
.Transactions {
overflow-x: hidden;
height: 100%;
background-color: white;
display: flex;
flex-direction: column;
@@ -26,7 +25,7 @@
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 20px;
font-size: 15px;
color: rgba(88, 55, 50, 1);
text-align: left;
margin-left: 20px;
@@ -40,17 +39,20 @@
}
.TotalContainer {
display: flex;
justify-content: space-between;
width: 100%; /* Ensures it takes up full width */
margin: 0 auto;
font-family: "Poppins", sans-serif;
font-weight: 600;
font-style: normal;
font-size: 1.5em;
padding: 10px;
box-sizing: border-box; /* Includes padding in width */
margin-bottom: 17px;
display: flex
;
justify-content: space-between;
/* width: 100%; */
margin: 0 auto;
font-family: "Poppins", sans-serif;
font-weight: 600;
font-style: normal;
font-size: 15px;
/* padding: 10px; */
box-sizing: border-box;
margin-bottom: 17px;
margin-left: 20px;
margin-right: 20px;
}
.PaymentContainer {
@@ -61,10 +63,9 @@
font-family: "Poppins", sans-serif;
font-weight: 600;
font-style: normal;
font-size: 1.5em;
font-size: 15px;
padding: 10px;
box-sizing: border-box; /* Includes padding in width */
margin-bottom: 17px;
justify-content: center;
}
@@ -72,8 +73,8 @@
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 70%; /* Adjusted for better readability */
padding: 12px 24px; /* Added padding for a better look */
font-size: 15px; /* Adjusted for better readability */
padding: 12px 16px; /* Added padding for a better look */
border-radius: 50px;
background-color: rgba(88, 55, 50, 1);
color: white;
@@ -87,7 +88,7 @@
font-family: "Poppins", sans-serif;
font-weight: 500;
font-style: normal;
font-size: 20px;
font-size: 15px;
padding: 12px 24px; /* Add some padding for spacing */
color: rgba(88, 55, 50, 1);
background-color: transparent; /* No background */
@@ -96,7 +97,6 @@
cursor: pointer;
display: block; /* Center the text horizontally */
text-align: center; /* Center the text within the button */
margin-bottom: 23px; /* Space at the bottom to match the PayButton */
}
.DeclineButton.active {
@@ -139,6 +139,7 @@
display: flex;
justify-content: space-between;
margin-left: 20px;
margin-right: 20px;
font-size: 1em;
margin-bottom: 15px;
}
@@ -155,6 +156,7 @@
}
.RibbonBanner {
pointer-events: none;
position: absolute;
top: 0;
width: 200px;
@@ -178,3 +180,95 @@
top: 60px;
left: -15px;
}
/* Header with logo and receipt info */
.receipt-header {
text-align: center;
}
.receipt-logo {
width: 80px;
height: 80px;
border-radius: 50%; /* Circular logo */
object-fit: cover;
margin-bottom: 10px;
}
.receipt-info h3 {
font-size: 16px;
margin: 5px 0;
}
.receipt-info p {
font-size: 14px;
margin: 2px 0;
}
/* Dotted line with circular cutouts */
.dotted-line {
display: flex;
align-items: center;
justify-content: center;
margin: 15px 0;
}
.dotted-line .line {
border-top: 13px dotted #dbdbdb;
width: 100%;
margin: 0 20px;
}
.dotted-line .circle-left {
left: -25px;
position: absolute;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #dbdbdb;
display: flex; /* Use flexbox to center the inner circle */
justify-content: center; /* Center horizontally */
align-items: center; /* Center vertically */
}
.dotted-line .circle-right {
right: -25px;
position: absolute;
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #dbdbdb;
display: flex; /* Use flexbox to center the inner circle */
justify-content: center; /* Center horizontally */
align-items: center; /* Center vertically */
}
.inner-circle {
width: 80%;
height: 80%;
border-radius: 50%; /* Make it a circle */
background-color: white; /* Background color of the inner circle */
}
.inner-text {
font-size: 24px; /* Adjust the font size as needed */
color: white; /* Text color */
}
.circle {
position: absolute;
display: inline-block;
width: 24px;
height: 24px;
padding: 4px;
border-radius: 50%;
margin-top: -43px;
margin-left: 43px;
/* Just making it pretty */
background: #38a9e4;
color: white;
font-family: Helvetica, Arial Black, sans;
font-size: 20px;
text-align: center;
}