Files
groovee/src/pages/MaterialList.js
zadit ba5c440a2a ok
2025-01-13 07:36:24 +07:00

364 lines
14 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'
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("");
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 [newPrice, setNewPrice] = 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);
setNewPrice(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 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 handleQuantityChange = (change) => {
setQuantityChange((prev) => prev + change);
if (quantityChange + change < 1) setNewPrice(currentPrice);
setIsEditCurrentPrice(false);
};
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));
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(newPrice)
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 className={styles.container}>
{loading ? (
<></>
) : (
<>
<h3 className={styles.title}>Bahan baku</h3>
<Carousel items={materials} onSelect={(e) => setSelectedMaterialIndex(e)} selectedIndex={selectedMaterialIndex} />
{selectedMaterialIndex !== -1 ? (
<>
<div className={styles.switchContainer}>
<h3>Stok sekarang {currentQuantity}</h3>
</div>
<div className={styles.stokContainer}>
<button onClick={() => handleQuantityChange(currentQuantity + quantityChange > 0 ? -1 : 0)} className={styles.stockButton}>
-
</button>
<p>{currentQuantity + quantityChange}</p>
<button onClick={() => handleQuantityChange(1)} className={styles.stockButton}>
+
</button>
</div>
<div className={styles.uploadMessage}>
<p>harga per {materials && materials[selectedMaterialIndex]?.unit} sekarang</p>
</div>
<div className={styles.resultMessage}>
<input
className={styles.resultMessageInput} // Replace inline style with CSS module class
disabled={!isEditCurrentPrice || quantityChange < 1}
value={newPrice}
onChange={handleChange}
placeholder="Enter amount"
/>
<div onClick={() => quantityChange < 1 ? null : setIsEditCurrentPrice(!isEditCurrentPrice)} className={quantityChange < 1 ? styles.changeButtonDisabled : styles.changeButtonEnabled}>
{isEditCurrentPrice ? 'Terapkan' : 'Ganti'}
</div>
</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 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} 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>Tidak ada laporan perubahan stok.</p>
)}
</div>
</div>
</>
)}
</div>
</>
) : (
<>
<div className={styles.description}>
<div style={{ marginRight: "5px", fontSize: "1.2em" }}></div>
<h6 style={{ margin: 0, textAlign: "left", fontSize: '12px' }}>
Fitur ini mempermudah mengelola biaya dan memantau pengeluaran bahan.
</h6>
</div>
<div className={styles.switchContainer}>
<h3>Buat bahan baru</h3>
</div>
<div className={styles.resultMessage}>
<input
className={styles.resultMessageInput}
value={newMaterialName}
onChange={(event) => setNewMaterialName(event.target.value)}
placeholder="Masukkan nama barang"
style={{width: '100%', height: '31px'}}
/>
</div>
<select
id="materialUnit"
value={newMaterialUnit}
onChange={(e) => setNewMaterialUnit(e.target.value)}
className={styles.unit}
style={{height: '37px'}}
>
<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 className={styles.buttonContainer}>
<button className={styles.saveButton} onClick={handleCreateMaterial}>
Buat bahan baku
</button>
</div>
</>
)}
</>
)}
</div>
);
};
export default SetPaymentQr;