493 lines
15 KiB
JavaScript
493 lines
15 KiB
JavaScript
import React, { useState, useRef, useEffect } from "react";
|
||
import jsQR from "jsqr";
|
||
import { getImageUrl } from "../helpers/itemHelper";
|
||
import {
|
||
getCafe,
|
||
saveCafeDetails,
|
||
setConfirmationStatus,
|
||
} from "../helpers/cafeHelpers";
|
||
|
||
import {
|
||
getMaterials,
|
||
createMaterial,
|
||
deleteMaterial,
|
||
} from "../helpers/materialHelpers";
|
||
import {
|
||
createMaterialMutation,
|
||
getMaterialMutations,
|
||
} from "../helpers/materialMutationHelpers";
|
||
|
||
import Switch from "react-switch"; // Import the Switch component
|
||
import Carousel from '../components/Carousel'
|
||
|
||
const SetPaymentQr = ({ cafeId }) => {
|
||
const [materials, setMaterials] = useState([]);
|
||
const [mutations, setMutations] = useState([]);
|
||
const [newMaterialName, setNewMaterialName] = useState("");
|
||
const [newMaterialUnit, setNewMaterialUnit] = useState("kilogram");
|
||
const [newMaterialImage, setNewMaterialImage] = useState(null);
|
||
const [deleting, setDeleting] = useState(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState(null);
|
||
const [showForm, setShowForm] = useState(false);
|
||
const [selectedMaterialIndex, setSelectedMaterialIndex] = useState(-1);
|
||
const [latestMutation, setLatestMutation] = useState([]);
|
||
const [currentQuantity, setCurrentQuantity] = useState(-1);
|
||
const [currentPrice, setCurrentPrice] = useState(0);
|
||
const [quantityChange, setQuantityChange] = useState(0);
|
||
const [sortOrder, setSortOrder] = useState("desc");
|
||
const [isEditCurrentPrice, setIsEditCurrentPrice] = useState(false);
|
||
const [isViewingHistory, setIsViewingHistory] = useState(false);
|
||
|
||
const convertToInteger = (formattedValue) => {
|
||
// Remove dots and convert to integer
|
||
return parseInt(formattedValue.replace(/\./g, ""), 10);
|
||
};
|
||
|
||
const formatCurrency = (value) => {
|
||
if (!value) return "";
|
||
// Remove existing formatting (dots) and format again
|
||
const numericValue = value.toString().replace(/\D/g, ""); // Keep only digits
|
||
return numericValue.replace(/\B(?=(\d{3})+(?!\d))/g, "."); // Add dot as thousands separator
|
||
};
|
||
|
||
const handleChange = (e) => {
|
||
const formattedValue = formatCurrency(e.target.value);
|
||
setCurrentPrice(formattedValue);
|
||
};
|
||
|
||
useEffect(() => {
|
||
const fetchMaterials = async () => {
|
||
try {
|
||
const data = await getMaterials(cafeId);
|
||
setMaterials(data);
|
||
console.log(data)
|
||
setError(null);
|
||
if (data.length > 0) {
|
||
setSelectedMaterialIndex(0);
|
||
}
|
||
} catch (error) {
|
||
console.error("Error fetching materials:", error);
|
||
setError("Failed to fetch materials.");
|
||
}
|
||
};
|
||
|
||
const fetchMutations = async () => {
|
||
try {
|
||
const data = await getMaterialMutations(cafeId);
|
||
setMutations(data);
|
||
} catch (err) {
|
||
setError(err.message);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
fetchMaterials();
|
||
fetchMutations();
|
||
}, [cafeId]);
|
||
|
||
const handleSortChange = (e) => {
|
||
setSortOrder(e.target.value);
|
||
};
|
||
|
||
const filteredMutations = mutations.filter((mutation) => mutation.materialId === materials[selectedMaterialIndex].materialId) || [];
|
||
|
||
const sortedMutations = filteredMutations
|
||
.filter((mutation) => mutation.materialId === materials[selectedMaterialIndex].materialId)
|
||
.sort((a, b) => {
|
||
if (sortOrder === "asc") {
|
||
return new Date(a.createdAt) - new Date(b.createdAt);
|
||
} else {
|
||
return new Date(b.createdAt) - new Date(a.createdAt);
|
||
}
|
||
});
|
||
|
||
const handleCreateMaterial = async (e) => {
|
||
e.preventDefault();
|
||
setLoading(true);
|
||
|
||
const formData = new FormData();
|
||
formData.append("name", newMaterialName);
|
||
formData.append("unit", newMaterialUnit);
|
||
if (newMaterialImage) {
|
||
formData.append("image", newMaterialImage);
|
||
}
|
||
|
||
try {
|
||
await createMaterial(cafeId, formData);
|
||
setNewMaterialName("");
|
||
setNewMaterialUnit("kilogram");
|
||
setNewMaterialImage(null);
|
||
setShowForm(false);
|
||
const data = await getMaterials(cafeId);
|
||
setMaterials(data);
|
||
setError(null);
|
||
if (data.length > 0) {
|
||
setSelectedMaterialIndex(0);
|
||
}
|
||
} catch (error) {
|
||
console.error("Error creating material:", error);
|
||
setError("Failed to create material.");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleDeleteMaterial = async (materialId) => {
|
||
setDeleting(materialId);
|
||
try {
|
||
await deleteMaterial(materialId);
|
||
const updatedMaterials = materials.filter(
|
||
(material) => material.materialId !== materialId
|
||
);
|
||
setMaterials(updatedMaterials);
|
||
setError(null);
|
||
if (selectedMaterialIndex === materialId) {
|
||
setSelectedMaterialIndex(
|
||
updatedMaterials.length > 0 ? updatedMaterials[0].materialId : null
|
||
);
|
||
}
|
||
} catch (error) {
|
||
console.error("Error deleting material:", error);
|
||
setError("Failed to delete material.");
|
||
} finally {
|
||
setDeleting(null);
|
||
}
|
||
};
|
||
|
||
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);
|
||
};
|
||
|
||
useEffect(() => {
|
||
setQuantityChange(0);
|
||
if (materials.length > 0 || selectedMaterialIndex > -1) {
|
||
const materialMutations = mutations.filter(
|
||
(mutation) => mutation.materialId === materials[selectedMaterialIndex]?.materialId
|
||
);
|
||
console.log(materialMutations)
|
||
if (materialMutations.length > 0) {
|
||
const latestMutation = materialMutations.reduce(
|
||
(latest, current) =>
|
||
new Date(current.createdAt) > new Date(latest.createdAt)
|
||
? current
|
||
: latest,
|
||
materialMutations[0]
|
||
);
|
||
setLatestMutation(latestMutation);
|
||
setCurrentQuantity(latestMutation.newStock);
|
||
setCurrentPrice(formatCurrency(latestMutation.priceAtp));
|
||
} else {
|
||
setCurrentQuantity(0); // Default value if no mutations exist
|
||
setLatestMutation({ newStock: 0 });
|
||
setCurrentPrice(0);
|
||
}
|
||
}
|
||
|
||
|
||
}, [materials, mutations, selectedMaterialIndex]);
|
||
|
||
const handleUpdateStock = async () => {
|
||
setLoading(true);
|
||
try {
|
||
const newPrice = convertToInteger(currentPrice)
|
||
const newStock = currentQuantity + quantityChange;
|
||
const formData = new FormData();
|
||
formData.append("newStock", newStock);
|
||
formData.append("priceAtp", newPrice);
|
||
formData.append("reason", "Stock update");
|
||
|
||
await createMaterialMutation(materials[selectedMaterialIndex].materialId, formData);
|
||
setQuantityChange(0);
|
||
const updatedMutations = await getMaterialMutations(cafeId);
|
||
setMutations(updatedMutations);
|
||
setCurrentQuantity(newStock);
|
||
setError(null);
|
||
} catch (error) {
|
||
console.error("Error updating stock:", error);
|
||
setError("Failed to update stock.");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
const currentMaterial = materials.find(
|
||
(material) => material.materialId === selectedMaterialIndex
|
||
);
|
||
const formatDate = (timestamp) => {
|
||
const date = new Date(timestamp);
|
||
return date.toLocaleString();
|
||
};
|
||
|
||
|
||
return (
|
||
<div style={styles.container}>
|
||
{loading ?
|
||
<>
|
||
</>
|
||
:
|
||
<>
|
||
<h3 style={styles.title}>Bahan baku</h3>
|
||
<Carousel items={materials} onSelect={(e) => setSelectedMaterialIndex(e)} selectedIndex={selectedMaterialIndex} />
|
||
{selectedMaterialIndex != -1 ?
|
||
<>
|
||
<div style={styles.switchContainer}>
|
||
<h3>Stok sekarang {currentQuantity}</h3>
|
||
</div>
|
||
|
||
<div style={styles.stokContainer}>
|
||
<button onClick={() => handleQuantityChange(currentQuantity + quantityChange > 0 ? -1 : 0)} style={styles.stockButton}>
|
||
-
|
||
</button>
|
||
<p>{currentQuantity + quantityChange}</p>
|
||
<button onClick={() => handleQuantityChange(1)} style={styles.stockButton}>
|
||
+
|
||
</button>
|
||
</div>
|
||
<div style={styles.uploadMessage}>
|
||
<p>harga per {materials && materials[selectedMaterialIndex]?.unit} sekarang</p>
|
||
</div>
|
||
<div style={styles.resultMessage}>
|
||
|
||
<input
|
||
|
||
style={{
|
||
width: "200px",
|
||
border: isEditCurrentPrice ? "1px solid #ccc" : "1px solid transparent",
|
||
backgroundColor: isEditCurrentPrice ? "white" : "transparent",
|
||
}}
|
||
disabled={!isEditCurrentPrice || quantityChange < 1}
|
||
value={currentPrice}
|
||
onChange={handleChange}
|
||
placeholder="Enter amount"
|
||
/>
|
||
<div onClick={() => quantityChange < 1 ? null : setIsEditCurrentPrice(!isEditCurrentPrice)} style={quantityChange < 1 ? styles.changeButtonDisabled : styles.changeButtonEnabled}>{isEditCurrentPrice ? 'Terapkan' : 'Ganti'}</div>
|
||
</div>
|
||
<div style={styles.buttonContainer}>
|
||
<button onClick={handleUpdateStock} style={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}>
|
||
{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>
|
||
))
|
||
) : (
|
||
<p>No mutations available.</p>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</> :
|
||
<>
|
||
<div
|
||
style={{ display: "flex", alignItems: "center", margin: "10px", marginTop: '17px', marginBottom: '34px' }}
|
||
>
|
||
<div style={{ marginRight: "5px", fontSize: "1.2em" }}>ⓘ</div>
|
||
<h6 style={{ margin: 0, textAlign: "left" }}>
|
||
Fitur ini mempermudah mengelola biaya dan memantau pengeluaran bahan.
|
||
</h6>
|
||
</div>
|
||
|
||
<div style={styles.switchContainer}>
|
||
<h3>Buat bahan baru</h3>
|
||
</div>
|
||
<div style={styles.resultMessage}>
|
||
|
||
<input
|
||
|
||
style={{
|
||
width: "100%",
|
||
height: '31px',
|
||
border: "1px solid #ccc"
|
||
}}
|
||
value={newMaterialName}
|
||
onChange={(event) => setNewMaterialName(event.target.value)}
|
||
placeholder="Masukkan nama barang"
|
||
/>
|
||
</div>
|
||
|
||
<select
|
||
id="materialUnit"
|
||
value={newMaterialUnit}
|
||
onChange={(e) => setNewMaterialUnit(e.target.value)}
|
||
style={styles.unit}
|
||
>
|
||
<option value="gram">Satuan: gram</option>
|
||
<option value="ons">Satuan: ons</option>
|
||
<option value="kilogram">Satuan: kilogram</option>
|
||
<option value="kuintal">Satuan: kuintal</option>
|
||
<option value="liter">Satuan: liter</option>
|
||
<option value="piece">Satuan: piece</option>
|
||
<option value="meter">Satuan: meter</option>
|
||
<option value="pack">Satuan: pack</option>
|
||
<option value="sachet">Satuan: sachet</option>
|
||
<option value="box">Satuan: box</option>
|
||
</select>
|
||
|
||
<div style={styles.buttonContainer}>
|
||
<button style={styles.saveButton}>
|
||
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;
|