Files
AnythingYouWant/src/pages/MaterialList.js
zadit 2864a00814 ok
2025-01-12 18:16:16 +07:00

493 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;