This commit is contained in:
zadit
2025-01-13 07:36:24 +07:00
parent 2864a00814
commit ba5c440a2a
7 changed files with 465 additions and 334 deletions

View File

@@ -386,7 +386,7 @@ const Header = ({
Desain kafe
</Child>
<Child onClick={() => setModal("edit_tables")}>
Daftar meja
QR kedai dan meja
</Child>
<Child onClick={() => setModal("payment_option")}>
Metode pembayaran

View File

@@ -67,6 +67,8 @@ const ItemLister = ({
const [itemsToCreate, setItemsToCreate] = useState([]);
const [itemsToUpdate, setItemsToUpdate] = useState([]);
const [isFirstStep, setIsFirstStep] = useState(true);
const handlePlusClick = (itemId) => {
const updatedItems = items.map((item) => {
if (item.itemId === itemId) {
@@ -483,6 +485,7 @@ const ItemLister = ({
setItemsToUpdate([]);
setIsEditing(false);
if (handleUnEdit) handleUnEdit();
setIsFirstStep(true);
};
return (
@@ -495,6 +498,8 @@ const ItemLister = ({
}`}
style={{ paddingBottom: isEdit ? "28vh" : "" }}
>
{ (isEdit && isFirstStep || !isEdit) &&
<div className={styles["title-container"]}>
{isEdit && <ItemType blank={true} imageUrl={previewUrl} />}
<input
@@ -590,7 +595,9 @@ const ItemLister = ({
</>
)}
</div>
{isEdit && (
}
{isEdit && isFirstStep && (
<>
<div className={styles["grid-container"]}>
<ItemType
rectangular={true}
@@ -744,8 +751,13 @@ const ItemLister = ({
);
})}
</div>
<button onClick={()=>setIsFirstStep(false)}style={{width: '100%', height: '40px', borderRadius: '20px'}}>selanjutnya</button>
</>
)}
{ (isEdit && !isFirstStep || !isEdit) &&
<>
{isEdit && <div style={{display: 'flex', justifyContent: 'space-between'}}><div onClick={()=>setIsFirstStep(true)}style={{color: 'black', fontSize: '50px', width: '30px'}}>&larr;</div>
<h3 style={{color: 'black'}}>Daftar item</h3><button style={{visibility: 'hidden', width: '30px'}}></button></div>}
<div className={styles["item-list"]}>
{user && (
user.userId == shopOwnerId || user.cafeId == shopId) &&
@@ -943,15 +955,18 @@ const ItemLister = ({
user.userId == shopOwnerId &&
isEdit && (
<>
<button
{/* <button
className={styles["add-item-button"]}
onClick={handleRemoveType}
>
Remove
</button>
</button> */}
</>
)}
</div>
</>
}
{isEdit && (
<div className={styles.PaymentOption}>
<div className={styles.TotalContainer}>

View File

@@ -22,11 +22,63 @@
grid-template-columns: repeat(4, 1fr);
gap: 10px;
/* padding: 10px; */
max-height: calc(3 * (25vw - 20px) + 20px);
/* max-height: calc(3 * (25vw - 20px) + 20px); */
overflow-y: auto;
height: calc(49vw - 20px);
}
@media (min-height: 0px) {
.grid-container {
height: 27vh;
}
}
@media (min-height: 630px) {
.grid-container {
height: 27vh;
}
}
@media (min-height: 636px) {
.grid-container {
height: 29vh;
}
}
@media (min-height: 650px) {
.grid-container {
height: 34vh;
}
}
@media (min-height: 705px) {
.grid-container {
height: 37vh;
}
}
@media (min-height: 735px) {
.grid-container {
height: 38vh;
}
}
@media (min-height: 759px) {
.grid-container {
height: 40vh;
}
}
@media (min-height: 819px) {
.grid-container {
height: 44vh;
}
}
@media (min-height: 830px) {
.grid-container {
height: 47vh;
}
}
@media (min-height: 892px) {
.grid-container {
height: 49vh;
}
}
.title-container {
display: flex;
align-items: center;
@@ -124,7 +176,6 @@
font-style: normal;
font-size: 1.5em;
padding: 10px 0;
margin-bottom: 17px;
}
.OptionContainer {
display: flex;
@@ -136,7 +187,6 @@
font-style: normal;
font-size: 0.9em;
padding: 10px 0;
margin-bottom: 17px;
}
.PayButton {
font-family: "Plus Jakarta Sans", sans-serif;

View File

@@ -85,7 +85,7 @@ const Modal = ({ user, shop, isOpen, onClose, modalContent, setModal, handleMove
{modalContent === "transaction_end" && <Transaction_end />}
{modalContent === "transaction_failed" && <Transaction_failed />}
{modalContent === "payment_option" && (
<PaymentOptions shopId={shop.cafeId} />
<PaymentOptions shop={shop} shopId={shop.cafeId} />
)}
{modalContent === "add_material" && (
<MaterialList handleClose={handleOverlayClick} cafeId={shop.cafeId} />

View File

@@ -1,6 +1,7 @@
import React, { useState, useRef, useEffect } from "react";
import jsQR from "jsqr";
import { getImageUrl } from "../helpers/itemHelper";
import { getTables, updateTable, createTable } from "../helpers/tableHelper";
import {
getCafe,
saveCafeDetails,
@@ -8,158 +9,195 @@ import {
} from "../helpers/cafeHelpers";
import Switch from "react-switch"; // Import the Switch component
const SetPaymentQr = ({ shopId }) => {
const [qrPosition, setQrPosition] = useState([50, 50]);
const [qrSize, setQrSize] = useState(50);
const [qrPayment, setQrPayment] = useState();
const [qrCodeDetected, setQrCodeDetected] = useState(false);
const [isNeedConfirmationState, setIsNeedConfirmationState] = useState(0);
const qrPaymentInputRef = useRef(null);
const qrCodeContainerRef = useRef(null);
const [cafe, setCafe] = useState({});
const SetPaymentQr = ({ shop }) => {
const [initialPos, setInitialPos] = useState({
left: shop.xposition,
top: shop.yposition,
});
const [initialSize, setInitialSize] = useState(shop.scale);
const [bgImageUrl, setBgImageUrl] = useState(getImageUrl(shop.qrBackground));
const qrBackgroundInputRef = useRef(null);
const shopUrl = window.location.hostname + "/" + shop.cafeId;
useEffect(() => {
const fetchCafe = async () => {
try {
const response = await getCafe(shopId);
setCafe(response);
setQrPayment(getImageUrl(response.qrPayment));
console.log(response.needsConfirmation);
setIsNeedConfirmationState(response.needsConfirmation === true ? 1 : 0); // Set state based on fetched data
setQrPosition([response.xposition, response.yposition]);
setQrSize(response.scale);
} catch (error) {
console.error("Error fetching cafe:", error);
const generateQRCodeUrl = (tableCode) => {
if (tableCode != null) {
return `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(
shopUrl + "/" + tableCode
)}`;
} else {
return `https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=${encodeURIComponent(
shopUrl
)}`;
}
};
fetchCafe();
}, [shopId]);
// Detect QR code when qrPayment updates
const [isConfig, setIsConfig] = useState(false);
const [isViewTables, setIsViewTables] = useState(false);
const [tables, setTables] = useState([]);
useEffect(() => {
if (qrPayment) {
detectQRCodeFromContainer();
const fetchData = async () => {
try {
console.log(shop);
const fetchedTables = await getTables(shop.cafeId);
setTables(fetchedTables);
} catch (error) {
console.error("Error fetching tables:", error);
}
}, [qrPayment]);
};
fetchData();
}, [shop.cafeId]);
const handleSave = () => {
const qrBackgroundFile = qrBackgroundInputRef.current.files[0]; // Get the selected file for qrBackground
// Prepare the details object
const details = {
qrSize: initialSize,
qrPosition: initialPos,
qrBackgroundFile, // Include qrBackground file
};
// Call saveCafeDetails function with the updated details object
saveCafeDetails(shop.cafeId, details)
.then((response) => {
console.log("Cafe details saved:", response);
})
.catch((error) => {
console.error("Error saving cafe details:", error);
});
};
const handlePositionChange = (e) => {
const { name, value } = e.target;
setInitialPos((prevPosition) => ({
...prevPosition,
[name]: parseFloat(value).toFixed(2),
}));
};
const handleSizeChange = (e) => {
setInitialSize(parseFloat(e.target.value).toFixed(2));
};
// Handle file input change
const handleFileChange = (e) => {
const file = e.target.files[0];
if (file) {
const newqrPayment = URL.createObjectURL(file);
setQrPayment(newqrPayment);
const newBgImage = URL.createObjectURL(file); // Create a temporary URL for display
setBgImageUrl(newBgImage);
}
};
// Detect QR code from the container
const detectQRCodeFromContainer = () => {
const container = qrCodeContainerRef.current;
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const img = new Image();
img.crossOrigin = "Anonymous";
img.onload = () => {
canvas.width = container.offsetWidth;
canvas.height = container.offsetHeight;
context.drawImage(img, 0, 0, canvas.width, canvas.height);
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const qrCode = jsQR(imageData.data, canvas.width, canvas.height);
setQrCodeDetected(!!qrCode);
if (qrCode) {
console.log("QR Code detected:", qrCode.data);
}
};
img.src = qrPayment;
};
// Save cafe details
const handleSave = async () => {
const qrPaymentFile = qrPaymentInputRef.current.files[0];
const details = {
qrPosition,
qrSize,
qrPaymentFile,
};
try {
const response = await saveCafeDetails(cafe.cafeId, details);
console.log("Cafe details saved:", response);
} catch (error) {
console.error("Error saving cafe details:", error);
}
try {
const response = await setConfirmationStatus(cafe.cafeId, isNeedConfirmationState === 1);
setIsNeedConfirmationState(response.needsConfirmation ? 1 : 0); // Update state after saving
} catch (error) {
console.error(error);
setIsNeedConfirmationState(cafe.needsConfirmation ? 1 : 0); // Fallback to initial value
}
};
// Handle Switch toggle
const handleChange = (checked) => {
setIsNeedConfirmationState(checked ? 1 : 0); // Toggle state based on the switch
};
const handleError = () => {
setQrPayment('https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSPsNr0TPq8dHT3nBwDQ6OHQQTrqzVFoeBOmuWfgyErrLbJi6f6CnnYhpNHEvkJ_2X-kyI&usqp=CAU'); // Set your fallback image here
};
return (
<div style={styles.container}>
<h3 style={styles.title}>Konfigurasi pembayaran</h3>
<div
id="qr-code-container"
ref={qrCodeContainerRef}
onClick={() => qrPaymentInputRef.current.click()}
style={{
background: `center center / contain no-repeat url(${bgImageUrl})`,
backgroundSize: "contain",
border: "1px solid #ddd",
...styles.qrCodeContainer,
backgroundImage: `url(${qrPayment})`,
backgroundImage: `url(${bgImageUrl})`,
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
backgroundSize: "contain",
}}
>
<img
src={qrPayment}
onError={handleError}
alt="QR Payment"
style={{ maxWidth: '100%',visibility: 'hidden' }} // hide the img element
/>
src={generateQRCodeUrl(shopUrl)}
alt="QR Code"
style={{
position: "absolute",
width: `${initialSize}%`,
left: `${initialPos.left}%`,
top: `${initialPos.top}%`,
transform: "translate(-50%, -50%)",
}} />
<input
type="file"
accept="image/*"
ref={qrPaymentInputRef}
ref={qrBackgroundInputRef}
style={{ display: "none" }}
onChange={handleFileChange}
/>
</div>
<div style={styles.uploadMessage}>
<p>Klik untuk unggah QRIS</p>
<p>Klik untuk ganti background</p>
</div>
<div style={styles.resultMessage}>
{qrCodeDetected && qrPayment !== 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSPsNr0TPq8dHT3nBwDQ6OHQQTrqzVFoeBOmuWfgyErrLbJi6f6CnnYhpNHEvkJ_2X-kyI&usqp=CAU' ? <p>QR terdeteksi</p> : <p>Tidak ada qr terdeteksi</p>}
{qrCodeDetected && qrPayment !== 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSPsNr0TPq8dHT3nBwDQ6OHQQTrqzVFoeBOmuWfgyErrLbJi6f6CnnYhpNHEvkJ_2X-kyI&usqp=CAU' ? <div
onClick={() => qrPaymentInputRef.current.click()} style={styles.uploadButton}>Ganti</div> : <div
onClick={() => qrPaymentInputRef.current.click()} style={styles.uploadButton}>Unggah</div>}
<p onClick={() => setIsConfig(!isConfig)}> {isConfig ? '˅' : '˃'} Konfigurasi QR</p>
<div
onClick={() => qrBackgroundInputRef.current.click()} style={styles.uploadButton}>Ganti</div>
</div>
<div style={styles.switchContainer}>
<h3>Pengecekan ketersediaan ganda</h3>
<p style={styles.description}>
Nyalakan agar kasir memeriksa kembali ketersediaan produk sebelum pelanggan membayar.
</p>
<Switch
onChange={handleChange}
checked={isNeedConfirmationState === 1} // Convert to boolean
offColor="#888"
onColor="#4CAF50"
uncheckedIcon={false}
checkedIcon={false}
height={25}
width={50}
{isConfig && <>
<div style={styles.sliderContainer}>
<label style={styles.label}>
QR Code Size:
<div style={styles.sliderWrapper}>
<span style={styles.labelStart}>10%</span>
<input
type="range"
step="0.25"
min="10"
max="100"
value={initialSize}
onChange={handleSizeChange}
style={styles.input}
/>
<span style={styles.value}>{initialSize}%</span>
</div>
</label>
</div>
<div style={styles.sliderContainer}>
<label style={styles.label}>
QR Code Position X:
<div style={styles.sliderWrapper}>
<span style={styles.labelStart}>0%</span>
<input
type="range"
step="0.25"
name="left"
min="0"
max="100"
value={initialPos.left}
onChange={handlePositionChange}
style={styles.input}
/>
<span style={styles.value}>{initialPos.left}%</span>
</div>
</label>
</div>
<div style={styles.sliderContainer}>
<label style={styles.label}>
QR Code Position Y:
<div style={styles.sliderWrapper}>
<span style={styles.labelStart}>0%</span>
<input
type="range"
step="0.25"
name="top"
min="0"
max="100"
value={initialPos.top}
onChange={handlePositionChange}
style={styles.input}
/>
<span style={styles.value}>{initialPos.top}%</span>
</div>
</label>
</div>
</>}
<p style={styles.description} onClick={() => setIsViewTables(!isViewTables)}>
{isViewTables ? '˅' : '˃'} Daftar meja
</p>
</div>
<div style={styles.buttonContainer}>
@@ -174,6 +212,7 @@ const SetPaymentQr = ({ shopId }) => {
// Styles
const styles = {
container: {
width: '100%',
backgroundColor: "white",
padding: "20px",
borderRadius: "8px",
@@ -230,13 +269,25 @@ const styles = {
transition: "background-color 0.3s",
},
switchContainer: {
marginTop: "20px",
textAlign: "left",
},
description: {
margin: "10px 0",
fontSize: "14px",
color: "#666",
},
sliderContainer: {
marginBottom: "20px",
},
label: {
display: "block",
marginBottom: "10px",
},
sliderWrapper: {
display: "flex",
alignItems: "center",
},
input: {
flex: "1",
margin: "0 10px",
},
};

View File

@@ -19,8 +19,10 @@ import {
import Switch from "react-switch"; // Import the Switch component
import Carousel from '../components/Carousel'
import styles from './MaterialList.module.css'; // Import the CSS Module
const SetPaymentQr = ({ cafeId }) => {
// All your state and logic goes here (unchanged)
const [materials, setMaterials] = useState([]);
const [mutations, setMutations] = useState([]);
const [newMaterialName, setNewMaterialName] = useState("");
@@ -34,6 +36,7 @@ const SetPaymentQr = ({ cafeId }) => {
const [latestMutation, setLatestMutation] = useState([]);
const [currentQuantity, setCurrentQuantity] = useState(-1);
const [currentPrice, setCurrentPrice] = useState(0);
const [newPrice, setNewPrice] = useState(0);
const [quantityChange, setQuantityChange] = useState(0);
const [sortOrder, setSortOrder] = useState("desc");
const [isEditCurrentPrice, setIsEditCurrentPrice] = useState(false);
@@ -53,7 +56,7 @@ const SetPaymentQr = ({ cafeId }) => {
const handleChange = (e) => {
const formattedValue = formatCurrency(e.target.value);
setCurrentPrice(formattedValue);
setNewPrice(formattedValue);
};
useEffect(() => {
@@ -87,11 +90,7 @@ const SetPaymentQr = ({ cafeId }) => {
fetchMutations();
}, [cafeId]);
const handleSortChange = (e) => {
setSortOrder(e.target.value);
};
const filteredMutations = mutations.filter((mutation) => mutation.materialId === materials[selectedMaterialIndex].materialId) || [];
const filteredMutations = mutations.filter((mutation) => mutation.materialId === materials[selectedMaterialIndex]?.materialId) || [];
const sortedMutations = filteredMutations
.filter((mutation) => mutation.materialId === materials[selectedMaterialIndex].materialId)
@@ -156,32 +155,11 @@ const SetPaymentQr = ({ cafeId }) => {
}
};
const handlePrevious = () => {
if (selectedMaterialIndex) {
setQuantityChange(0);
const currentIndex = materials.findIndex(
(material) => material.materialId === selectedMaterialIndex
);
if (currentIndex > 0) {
setSelectedMaterialIndex(materials[currentIndex - 1].materialId);
}
}
};
const handleNext = () => {
if (selectedMaterialIndex) {
setQuantityChange(0);
const currentIndex = materials.findIndex(
(material) => material.materialId === selectedMaterialIndex
);
if (currentIndex < materials.length - 1) {
setSelectedMaterialIndex(materials[currentIndex + 1].materialId);
}
}
};
const handleQuantityChange = (change) => {
setQuantityChange((prev) => prev + change);
if (quantityChange + change < 1) setNewPrice(currentPrice);
setIsEditCurrentPrice(false);
};
useEffect(() => {
@@ -202,24 +180,27 @@ const SetPaymentQr = ({ cafeId }) => {
setLatestMutation(latestMutation);
setCurrentQuantity(latestMutation.newStock);
setCurrentPrice(formatCurrency(latestMutation.priceAtp));
setNewPrice(formatCurrency(latestMutation.priceAtp));
} else {
setCurrentQuantity(0); // Default value if no mutations exist
setLatestMutation({ newStock: 0 });
setCurrentPrice(0);
setNewPrice(0);
}
}
setIsViewingHistory(false);
}, [materials, mutations, selectedMaterialIndex]);
const handleUpdateStock = async () => {
setLoading(true);
try {
const newPrice = convertToInteger(currentPrice)
const newprice = convertToInteger(newPrice)
const newStock = currentQuantity + quantityChange;
const formData = new FormData();
formData.append("newStock", newStock);
formData.append("priceAtp", newPrice);
formData.append("priceAtp", newprice);
formData.append("reason", "Stock update");
await createMaterialMutation(materials[selectedMaterialIndex].materialId, formData);
@@ -244,103 +225,106 @@ const SetPaymentQr = ({ cafeId }) => {
return date.toLocaleString();
};
return (
<div style={styles.container}>
{loading ?
<div className={styles.container}>
{loading ? (
<></>
) : (
<>
</>
:
<>
<h3 style={styles.title}>Bahan baku</h3>
<h3 className={styles.title}>Bahan baku</h3>
<Carousel items={materials} onSelect={(e) => setSelectedMaterialIndex(e)} selectedIndex={selectedMaterialIndex} />
{selectedMaterialIndex != -1 ?
{selectedMaterialIndex !== -1 ? (
<>
<div style={styles.switchContainer}>
<div className={styles.switchContainer}>
<h3>Stok sekarang {currentQuantity}</h3>
</div>
<div style={styles.stokContainer}>
<button onClick={() => handleQuantityChange(currentQuantity + quantityChange > 0 ? -1 : 0)} style={styles.stockButton}>
<div className={styles.stokContainer}>
<button onClick={() => handleQuantityChange(currentQuantity + quantityChange > 0 ? -1 : 0)} className={styles.stockButton}>
-
</button>
<p>{currentQuantity + quantityChange}</p>
<button onClick={() => handleQuantityChange(1)} style={styles.stockButton}>
<button onClick={() => handleQuantityChange(1)} className={styles.stockButton}>
+
</button>
</div>
<div style={styles.uploadMessage}>
<div className={styles.uploadMessage}>
<p>harga per {materials && materials[selectedMaterialIndex]?.unit} sekarang</p>
</div>
<div style={styles.resultMessage}>
<div className={styles.resultMessage}>
<input
style={{
width: "200px",
border: isEditCurrentPrice ? "1px solid #ccc" : "1px solid transparent",
backgroundColor: isEditCurrentPrice ? "white" : "transparent",
}}
className={styles.resultMessageInput} // Replace inline style with CSS module class
disabled={!isEditCurrentPrice || quantityChange < 1}
value={currentPrice}
value={newPrice}
onChange={handleChange}
placeholder="Enter amount"
/>
<div onClick={() => quantityChange < 1 ? null : setIsEditCurrentPrice(!isEditCurrentPrice)} style={quantityChange < 1 ? styles.changeButtonDisabled : styles.changeButtonEnabled}>{isEditCurrentPrice ? 'Terapkan' : 'Ganti'}</div>
<div onClick={() => quantityChange < 1 ? null : setIsEditCurrentPrice(!isEditCurrentPrice)} className={quantityChange < 1 ? styles.changeButtonDisabled : styles.changeButtonEnabled}>
{isEditCurrentPrice ? 'Terapkan' : 'Ganti'}
</div>
<div style={styles.buttonContainer}>
<button onClick={handleUpdateStock} style={styles.saveButton}>
</div>
<div className={styles.buttonContainer}>
<button onClick={handleUpdateStock} className={styles.saveButton}>
Laporkan {quantityChange > 0 ? 'penambahan' : 'stok sekarang'} {quantityChange < 1 ? currentQuantity + quantityChange : quantityChange} {materials[selectedMaterialIndex]?.unit}
</button>
</div>
<div onClick={() => setIsViewingHistory(!isViewingHistory)} style={styles.historyTab}>
<h3> {isViewingHistory ? '˅' : '˃'} Riwayat stok</h3>
<div style={styles.historyContainer}>
{selectedMaterialIndex != -1 && isViewingHistory && !loading && (
<div style={styles.mutationContainer}>
<div className={styles.historyTab}>
<h3 onClick={() => setIsViewingHistory(!isViewingHistory)}> {isViewingHistory ? '˅' : '˃'} Riwayat stok</h3>
{selectedMaterialIndex !== -1 && isViewingHistory && !loading && (
<>
<div className={styles.sorter} onClick={() => setSortOrder(sortOrder == 'asc' ? 'desc' : 'asc')}>
Urutkan: {sortOrder === 'asc' ? "terlama" : "terbaru"} <div style={{ transform: 'rotate(90deg)' }}>&lt;&gt;</div>
</div>
<div className={styles.historyContainer}>
<div className={styles.mutationContainer}>
{sortedMutations.length > 0 ? (
sortedMutations.map((mutation) => (
<div key={mutation.id} style={styles.mutationCard}>
<h4 style={styles.mutationTitle}>
{formatDate(mutation.createdAt)}
</h4>
<p>Details: {mutation.reason}</p>
<p>stok {mutation.newStock}</p>
<div key={mutation.id} className={styles.mutationCard}>
<div style={{ width: '42px', backgroundColor: '#b9b9b9', borderRadius: '10px', padding: '3px', paddingBottom: '0' }}>
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
<g id="SVGRepo_iconCarrier">
<g id="Interface / Book">
<path id="Vector" d="M5 19.5002V6.2002C5 5.08009 5 4.51962 5.21799 4.0918C5.40973 3.71547 5.71547 3.40973 6.0918 3.21799C6.51962 3 7.08009 3 8.2002 3H17.4002C17.9602 3 18.2407 3 18.4546 3.10899C18.6427 3.20487 18.7948 3.35774 18.8906 3.5459C18.9996 3.75981 19 4.04005 19 4.6001V16.4001C19 16.9601 18.9996 17.2398 18.8906 17.4537C18.7948 17.6419 18.6429 17.7952 18.4548 17.8911C18.2411 18 17.961 18 17.402 18H7.25C6.00736 18 5 19.0074 5 20.25C5 20.6642 5.33579 21 5.75 21H16.402C16.961 21 17.2411 21 17.4548 20.8911C17.6429 20.7952 17.7948 20.642 17.8906 20.4538C17.9996 20.2399 18 19.9601 18 19.4V18" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
</g>
</svg>
</div>
<div className={styles.mutationTitle}>
<h4>{formatDate(mutation.createdAt)}</h4>
<p>Total stok: {mutation.newStock} || +{mutation.newStock - mutation.oldStock} || {formatCurrency((mutation.newStock - mutation.oldStock) * mutation.priceAtp)}</p>
</div>
</div>
))
) : (
<p>No mutations available.</p>
)}
</div>
<p>Tidak ada laporan perubahan stok.</p>
)}
</div>
</div>
</> :
</>
)}
</div>
</>
) : (
<>
<div
style={{ display: "flex", alignItems: "center", margin: "10px", marginTop: '17px', marginBottom: '34px' }}
>
<div className={styles.description}>
<div style={{ marginRight: "5px", fontSize: "1.2em" }}></div>
<h6 style={{ margin: 0, textAlign: "left" }}>
<h6 style={{ margin: 0, textAlign: "left", fontSize: '12px' }}>
Fitur ini mempermudah mengelola biaya dan memantau pengeluaran bahan.
</h6>
</div>
<div style={styles.switchContainer}>
<div className={styles.switchContainer}>
<h3>Buat bahan baru</h3>
</div>
<div style={styles.resultMessage}>
<div className={styles.resultMessage}>
<input
style={{
width: "100%",
height: '31px',
border: "1px solid #ccc"
}}
className={styles.resultMessageInput}
value={newMaterialName}
onChange={(event) => setNewMaterialName(event.target.value)}
placeholder="Masukkan nama barang"
style={{width: '100%', height: '31px'}}
/>
</div>
@@ -348,7 +332,8 @@ const SetPaymentQr = ({ cafeId }) => {
id="materialUnit"
value={newMaterialUnit}
onChange={(e) => setNewMaterialUnit(e.target.value)}
style={styles.unit}
className={styles.unit}
style={{height: '37px'}}
>
<option value="gram">Satuan: gram</option>
<option value="ons">Satuan: ons</option>
@@ -362,131 +347,17 @@ const SetPaymentQr = ({ cafeId }) => {
<option value="box">Satuan: box</option>
</select>
<div style={styles.buttonContainer}>
<button style={styles.saveButton}>
<div className={styles.buttonContainer}>
<button className={styles.saveButton} onClick={handleCreateMaterial}>
Buat bahan baku
</button>
</div>
</>
}
)}
</>
}
)}
</div>
);
};
// Styles
const styles = {
container: {
width: '100%',
minHeight: '50vh',
backgroundColor: "white",
padding: "20px",
borderRadius: "8px",
boxShadow: "0 2px 10px rgba(0, 0, 0, 0.1)",
textAlign: "center", // Center text and children
},
title: {
marginBottom: "20px",
fontWeight: "bold",
},
qrCodeContainer: {
backgroundColor: '#999999',
borderRadius: '20px',
position: "relative",
width: "100%",
height: "200px",
backgroundSize: "contain",
overflow: "hidden",
margin: "0 auto", // Center the QR code container
},
uploadMessage: {
fontWeight: 600,
textAlign: "left",
},
changeButtonEnabled: {
paddingRight: '10px',
backgroundColor: 'green',
borderRadius: '30px',
color: 'white',
fontWeight: 700,
height: '36px',
lineHeight: '36px',
paddingLeft: '10px',
paddingHeight: '10px',
},
changeButtonDisabled: {
paddingRight: '10px',
backgroundColor: '#a1a1a1',
borderRadius: '30px',
color: 'white',
fontWeight: 700,
height: '36px',
lineHeight: '36px',
paddingLeft: '10px',
paddingHeight: '10px',
},
resultMessage: {
marginTop: "-13px",
textAlign: "left",
display: 'flex',
justifyContent: 'space-between'
},
stokContainer: {
display: 'flex',
justifyContent: 'space-evenly',
alignItems: 'center',
marginTop: '18px',
marginBottom: "6px",
textAlign: "left",
},
buttonContainer: {
marginTop: "11px",
textAlign: "left",
},
stockButton: {
padding: "10px 20px",
fontSize: "3.5vw",
backgroundColor: "#28a745",
color: "#fff",
border: "none",
borderRadius: "30px",
cursor: "pointer",
transition: "background-color 0.3s",
},
saveButton: {
width: '100%',
padding: "10px 20px",
fontSize: "3.5vw",
backgroundColor: "#28a745",
color: "#fff",
border: "none",
borderRadius: "30px",
cursor: "pointer",
transition: "background-color 0.3s",
},
switchContainer: {
marginTop: "20px",
textAlign: "left",
},
historyTab: {
textAlign: "left",
},
historyContainer: {
textAlign: "left",
maxHeight: '200px',
overflowY: 'auto'
},
description: {
margin: "10px 0",
fontSize: "14px",
color: "#666",
},
unit: {
marginTop: '11px',
width: '100%',
height: '31px',
},
};
export default SetPaymentQr;

View File

@@ -0,0 +1,144 @@
/* SetPaymentQr.module.css */
.container {
width: 100%;
min-height: 47vh;
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
text-align: center;
}
.title {
margin-bottom: 20px;
font-weight: bold;
}
.uploadMessage {
font-weight: 600;
text-align: left;
}
.changeButtonEnabled {
padding-right: 10px;
background-color: green;
border-radius: 30px;
color: white;
font-weight: 700;
height: 36px;
line-height: 36px;
padding-left: 10px;
padding-height: 10px;
}
.changeButtonDisabled {
padding-right: 10px;
background-color: #a1a1a1;
border-radius: 30px;
color: white;
font-weight: 700;
height: 36px;
line-height: 36px;
padding-left: 10px;
padding-height: 10px;
}
.resultMessage {
margin-top: -13px;
text-align: left;
display: flex;
justify-content: space-between;
}
.stokContainer {
display: flex;
justify-content: space-evenly;
align-items: center;
margin-top: -20px;
margin-bottom: -15px;
text-align: left;
}
.buttonContainer {
margin-top: 11px;
text-align: left;
}
.stockButton {
padding: 10px 20px;
font-size: 3.5vw;
background-color: #28a745;
color: #fff;
border: none;
border-radius: 30px;
cursor: pointer;
transition: background-color 0.3s;
}
.saveButton {
width: 100%;
padding: 10px 20px;
font-size: 3.5vw;
background-color: #28a745;
color: #fff;
border: none;
border-radius: 30px;
cursor: pointer;
transition: background-color 0.3s;
}
.switchContainer {
margin-top: 20px;
text-align: left;
}
.historyTab {
text-align: left;
}
.historyContainer {
text-align: left;
max-height: 15vh;
overflow-y: auto;
}
.description {
display: flex;
margin: 10px 0;
font-size: 14px;
color: #666;
}
.unit {
margin-top: 11px;
width: 100%;
height: 31px;
}
.sorter {
border: 1px solid #c3c3c3;
padding: 5px;
border-radius: 10px;
display: flex;
justify-content: space-between;
margin-top: -10px;
margin-bottom: 10px;
}
.mutationCard {
display: flex;
margin-bottom: 7px;
margin-top: 7px;
}
.mutationTitle {
margin-left: 5px;
}
.mutationTitle h4 {
margin: 0;
}
.mutationTitle p {
margin: 0;
}