MIF_E31222881/resources/js/Components/ModalInput.jsx

319 lines
15 KiB
JavaScript

import { Inertia } from "@inertiajs/inertia";
import React, { useState, useEffect } from "react";
const getImageUrl = (foto) => {
if (!foto) return null;
if (typeof foto === 'string') return `${foto}`;
return URL.createObjectURL(foto);
};
const ModalInput = ({ fields, tableName, options, initialData, onClose, showPayments = false }) => {
const [formData, setFormData] = useState({});
const [errors, setErrors] = useState({});
const [selectedPayments, setSelectedPayments] = useState([]);
const [paymentDetails, setPaymentDetails] = useState({});
useEffect(() => {
// console.log(initialData)
setFormData(initialData || {});
setSelectedPayments([]);
setPaymentDetails({});
}, [initialData]);
const handleChange = (e) => {
if (e.target.type === "file") {
setFormData({ ...formData, [e.target.name]: e.target.files[0] });
} else {
setFormData({ ...formData, [e.target.name]: e.target.value });
}
};
// console.log(initialData)
const handlePaymentChange = (e) => {
const typeId = e.target.value;
console.log(typeId)
if (!selectedPayments.includes(typeId)) {
setSelectedPayments([...selectedPayments, typeId]);
const nominal = parseInt(options.payment_nominal[typeId]) || 0;
const penalty = parseInt(options.payment_penalty[typeId]) || 0;
setPaymentDetails({
...paymentDetails,
[typeId]: {
range: 1,
nominal,
penalty,
amount: nominal + penalty,
},
});
}
};
const handleRangeChange = (paymentType, newRange) => {
setPaymentDetails((prevDetails) => {
const validRange = newRange && !isNaN(newRange) ? Math.max(1, Number(newRange)) : 1;
const currentDetails = prevDetails[paymentType] || { nominal: 0, penalty: 0 };
const newAmount = (currentDetails.nominal + currentDetails.penalty) * validRange;
return {
...prevDetails,
[paymentType]: {
...currentDetails,
range: validRange,
amount: newAmount,
},
};
});
};
const handleRemovePayment = (paymentType) => {
setSelectedPayments(selectedPayments.filter((p) => p !== paymentType));
const newDetails = { ...paymentDetails };
delete newDetails[paymentType];
setPaymentDetails(newDetails);
};
const handleSubmit = (e) => {
e.preventDefault();
const formDataObj = new FormData();
Object.keys(formData).forEach((key) => {
if (key === 'foto' && !(formData[key] instanceof File)) {
return;
}
formDataObj.append(key, formData[key]);
});
if (showPayments) {
const detailsArray = Object.entries(paymentDetails).map(([type_id, detail]) => ({
...detail,
type_id: parseInt(type_id)
}));
// Append items satu per satu
detailsArray.forEach((item, index) => {
formDataObj.append(`items[${index}][type_id]`, item.type_id);
formDataObj.append(`items[${index}][range]`, item.range);
// Kalau mau append nominal & penalty juga bisa, tapi biasanya backend gak butuh karena bisa hitung ulang
});
}
const url = initialData
? `/update${tableName}/${initialData.id}`
: `/add${tableName}`;
Inertia.post(url, formDataObj, {
forceFormData: true,
onError: (errors) => setErrors(errors),
onSuccess: () => {
document.getElementById('modal_input').checked = false;
setFormData({});
setErrors({});
onClose({});
},
});
};
useEffect(() => {
if (formData.foto instanceof File) {
const url = URL.createObjectURL(formData.foto);
return () => URL.revokeObjectURL(url);
}
}, [formData.foto]);
return (
<div>
<input type="checkbox" id="modal_input" className="modal-toggle" />
<div className="modal" role="dialog">
<div className="modal-box max-w-lg">
<h2 className="font-bold text-xl text-center mb-6 border-b pb-2">
Form Data
</h2>
<form onSubmit={handleSubmit} encType="multipart/form-data" className="space-y-4">
{Object.entries(fields).map(([field, config]) => {
const type = typeof config === "string" ? config : config.type;
const readOnly = typeof config === "object" && config.readonly;
return (
<div key={field} className="form-control">
<label className="label font-semibold capitalize">{field.replace("_", " ")}</label>
{type === "select" ? (
<select
name={field}
value={formData[field] || ""}
onChange={handleChange}
className="select select-info focus:outline-none focus:ring-1 ring-info w-full"
>
<option disabled value="">
Pilih {field.replace("_", " ")}
</option>
{options[field] &&
Object.entries(options[field]).map(([key, value]) => (
<option key={key} value={key}>
{value}
</option>
))}
</select>
) : type === "file" ? (
<div>
<input
type="file"
name={field}
onChange={handleChange}
className="file-input file-input-info focus:outline-none focus:ring-1 ring-info w-full"
/>
{field === 'foto' && (
<div className="mt-2">
{formData.foto && (
<img
src={getImageUrl(formData.foto)}
alt="Preview Foto"
className="w-32 h-32 object-cover rounded-lg border"
/>
)}
</div>
)}
</div>
) : type === "password" ? (
<input
type="password"
name={field}
onChange={handleChange}
value={formData[field] || ""}
className="input input-info focus:outline-none focus:ring-1 ring-info w-full"
placeholder={
initialData
? 'Kosongkan jika tidak ingin mengubah password'
: 'Masukkan password broh'
}
/>
) : (
<input
type={type}
name={field}
value={formData[field] || ""}
onChange={handleChange}
readOnly={readOnly}
className="input input-info focus:outline-none focus:ring-1 ring-info w-full"
placeholder={`Masukkan ${field.replace("_", " ")}`}
/>
)}
{errors[field] && (
<p className="text-red-500 text-sm mt-1">{errors[field]}</p>
)}
</div>
);
})}
{showPayments && (
<div className="space-y-4 mt-4">
<div className="form-control">
<label className="label font-semibold">Payment Type</label>
<select
onChange={handlePaymentChange}
className="select select-info focus:outline-none focus:ring-1 ring-info w-full"
>
<option disabled value="">
Pilih Payment Type
</option>
{options.payment_type &&
Object.entries(options.payment_type).map(([id, name]) => (
<option key={id} value={id}>
{name}
</option>
))}
</select>
</div>
{selectedPayments.map((paymentType) => (
<div
key={paymentType}
className="border rounded-lg p-4 relative bg-gray-50 shadow-sm"
>
<button
type="button"
onClick={() => handleRemovePayment(paymentType)}
className="absolute top-2 right-2 bg-red-500 hover:bg-red-600 text-white px-2 py-1 rounded-full text-xs"
>
X
</button>
<div className="form-control mb-2">
<label className="label font-semibold">
{options.payment_type[paymentType]} - Range Bulan
</label>
<input
type="number"
min="1"
value={paymentDetails[paymentType].range}
onChange={(e) =>
handleRangeChange(paymentType, parseInt(e.target.value))
}
className="input input-info w-full"
name="range"
/>
</div>
<div className="form-control mb-2">
<label className="label font-semibold">Nominal</label>
<input
type="text"
value={paymentDetails[paymentType].nominal}
readOnly
className="input bg-gray-100 w-full"
/>
</div>
<div className="form-control mb-2">
<label className="label font-semibold">Penalty</label>
<input
type="text"
value={paymentDetails[paymentType].penalty}
readOnly
className="input bg-gray-100 w-full"
/>
</div>
</div>
))}
<div className="form-control mt-4">
<label className="label font-semibold">Total Amount</label>
<input
type="text"
value={Object.values(paymentDetails).reduce(
(sum, p) => sum + (p.amount || 0),
0
)}
readOnly
className="input bg-gray-100 w-full"
/>
</div>
</div>
)}
<button type="submit" className="btn btn-info text-white font-bold w-full mt-4">
{initialData ? "submit" : "Tambah Data"}
</button>
</form>
</div>
<label className="modal-backdrop" htmlFor="modal_input">
Close
</label>
</div>
</div>
);
};
export default ModalInput