Merge pull request #13 from alealien666/manualPay
insyaallah done manual payment fe
This commit is contained in:
commit
6600397b3f
|
@ -5,9 +5,12 @@
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Models\Santri;
|
use App\Models\Santri;
|
||||||
|
use App\Models\PaymentType;
|
||||||
|
use App\Models\DetailPayment;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use App\Services\cekDenda;
|
use App\Services\cekDenda;
|
||||||
use App\Services\GenerateMonthlyBill;
|
use App\Services\GenerateMonthlyBill;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
class PaymentController extends Controller
|
class PaymentController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -16,16 +19,18 @@ public function indexManualPayment(cekDenda $cekDenda, GenerateMonthlyBill $gene
|
||||||
$penalty = $cekDenda->applyPenalty();
|
$penalty = $cekDenda->applyPenalty();
|
||||||
$bill = $generateMonthlyBill->generateAutoBill();
|
$bill = $generateMonthlyBill->generateAutoBill();
|
||||||
|
|
||||||
|
$paymentType = PaymentType::pluck('payment_type');
|
||||||
|
$nominal = PaymentType::pluck('nominal');
|
||||||
|
|
||||||
|
$paymentPenalties = DetailPayment::with('paymentType')
|
||||||
|
->get()
|
||||||
|
->pluck('penalty', 'type_id');
|
||||||
|
|
||||||
$santri = Santri::with([
|
$santri = Santri::with([
|
||||||
'payments.detailPayments.paymentType',
|
'payments.detailPayments.paymentType',
|
||||||
'user'
|
'user'
|
||||||
])->get();
|
])->get();
|
||||||
|
|
||||||
// $santri = Payment::with('detailPayments')->get();
|
|
||||||
|
|
||||||
// dd($santri);
|
|
||||||
|
|
||||||
return Inertia::render('list-admin/payment/ManualPayment', [
|
return Inertia::render('list-admin/payment/ManualPayment', [
|
||||||
'santri' => $santri,
|
'santri' => $santri,
|
||||||
'penalty' => $penalty,
|
'penalty' => $penalty,
|
||||||
|
@ -35,11 +40,30 @@ public function indexManualPayment(cekDenda $cekDenda, GenerateMonthlyBill $gene
|
||||||
'nama' => 'text',
|
'nama' => 'text',
|
||||||
'status_santri' => 'text',
|
'status_santri' => 'text',
|
||||||
'role_santri' => 'text',
|
'role_santri' => 'text',
|
||||||
'total_penalty' => 'text',
|
// 'total_penalty' => 'text',
|
||||||
'amount' => 'text',
|
// 'amount_payment' => 'text',
|
||||||
|
// 'nominal' => 'text',
|
||||||
|
// 'payment_type' => 'select',
|
||||||
|
],
|
||||||
|
'options' => [
|
||||||
|
'payment_type' => $paymentType,
|
||||||
|
'payment_nominal' => $nominal,
|
||||||
|
'payment_penalty' => $paymentPenalties
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function manualPayment() {}
|
public function manualPayment(Request $request, $id)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'amount' => 'required',
|
||||||
|
'penalty' => 'nullable'
|
||||||
|
], [
|
||||||
|
'amount.required' => 'wajib mengisi nominal pembayaran',
|
||||||
|
]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
} catch (Exception $e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public function up(): void
|
||||||
Schema::create('payment_types', function (Blueprint $table) {
|
Schema::create('payment_types', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('payment_type');
|
$table->string('payment_type');
|
||||||
$table->float('amount');
|
$table->float('nominal');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public function up(): void
|
||||||
Schema::create('payments', function (Blueprint $table) {
|
Schema::create('payments', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->enum('payment_status', ['pending', 'failed', 'success']);
|
$table->enum('payment_status', ['pending', 'failed', 'success']);
|
||||||
$table->float('amount_payment');
|
$table->float('amount_payment')->nullable();
|
||||||
$table->String('bank')->nullable();
|
$table->String('bank')->nullable();
|
||||||
$table->string('no_va')->nullable();
|
$table->string('no_va')->nullable();
|
||||||
$table->dateTime('expired_at')->nullable();
|
$table->dateTime('expired_at')->nullable();
|
||||||
|
|
|
@ -4,9 +4,13 @@ import React, { useState, useEffect } from "react";
|
||||||
const ModalInput = ({ fields, tableName, options, initialData, onClose }) => {
|
const ModalInput = ({ fields, tableName, options, initialData, onClose }) => {
|
||||||
const [formData, setFormData] = useState({});
|
const [formData, setFormData] = useState({});
|
||||||
const [errors, setErrors] = useState({});
|
const [errors, setErrors] = useState({});
|
||||||
|
const [selectedPayments, setSelectedPayments] = useState([]);
|
||||||
|
const [paymentDetails, setPaymentDetails] = useState({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFormData(initialData || {})
|
setFormData(initialData || {});
|
||||||
|
setSelectedPayments([]);
|
||||||
|
setPaymentDetails({});
|
||||||
}, [initialData]);
|
}, [initialData]);
|
||||||
|
|
||||||
const handleChange = (e) => {
|
const handleChange = (e) => {
|
||||||
|
@ -17,41 +21,75 @@ const ModalInput = ({ fields, tableName, options, initialData, onClose }) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePaymentChange = (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
if (!selectedPayments.includes(value)) {
|
||||||
|
setSelectedPayments([...selectedPayments, value]);
|
||||||
|
|
||||||
|
const nominal = options.payment_nominal?.[value] || 0;
|
||||||
|
const penalty = options.payment_penalty?.[value] || 0;
|
||||||
|
|
||||||
|
setPaymentDetails({
|
||||||
|
...paymentDetails,
|
||||||
|
[value]: {
|
||||||
|
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, // Pastikan amount diperbarui
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleRemovePayment = (paymentType) => {
|
||||||
|
setSelectedPayments(selectedPayments.filter((p) => p !== paymentType));
|
||||||
|
const newDetails = { ...paymentDetails };
|
||||||
|
delete newDetails[paymentType];
|
||||||
|
setPaymentDetails(newDetails);
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
// console.log("tableName:", tableName);
|
const formDataObj = new FormData();
|
||||||
|
|
||||||
const formDataObj = new FormData()
|
Object.entries(formData).forEach(([key, value]) => {
|
||||||
Object.keys(formData).forEach((key) => {
|
if (key === 'foto' && !(value instanceof File)) return;
|
||||||
if (key === 'foto' && !(formData[key] instanceof File)) {
|
formDataObj.append(key, value);
|
||||||
return
|
});
|
||||||
}
|
|
||||||
formDataObj.append(key, formData[key])
|
|
||||||
})
|
|
||||||
|
|
||||||
if (initialData) {
|
formDataObj.append("payments", JSON.stringify(paymentDetails));
|
||||||
Inertia.post(`/update${tableName}/${initialData.id}`, formDataObj, {
|
|
||||||
|
const url = initialData ? `/update${tableName}/${initialData.id}` : `/add${tableName}`;
|
||||||
|
Inertia.post(url, formDataObj, {
|
||||||
forceFormData: true,
|
forceFormData: true,
|
||||||
onError: (errors) => setErrors(errors),
|
onError: (errors) => setErrors(errors),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
document.getElementById('modal_input').checked = false
|
document.getElementById("modal_input").checked = false;
|
||||||
setFormData({})
|
setFormData({});
|
||||||
setErrors({})
|
setErrors({});
|
||||||
onClose({})
|
if (onClose) onClose({});
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
} else {
|
};
|
||||||
Inertia.post(`/add${tableName}`, formDataObj, {
|
|
||||||
forceFormData: true,
|
|
||||||
onError: (errors) => setErrors(errors),
|
|
||||||
onSuccess: () => {
|
|
||||||
document.getElementById('modal_input').checked = false
|
|
||||||
setFormData({})
|
|
||||||
setErrors({})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -102,7 +140,73 @@ const ModalInput = ({ fields, tableName, options, initialData, onClose }) => {
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<button type="submit" className="btn btn-secondary">
|
<div className="mb-2">
|
||||||
|
<label className="input input-bordered input-secondary flex items-center gap-2">
|
||||||
|
Payment Type
|
||||||
|
<select onChange={handlePaymentChange} className="select select-bordered w-full select-secondary">
|
||||||
|
<option disabled value="">Pilih Payment Type</option>
|
||||||
|
{options.payment_type &&
|
||||||
|
Object.entries(options.payment_type).map(([key, value]) => (
|
||||||
|
<option key={key} value={key}>{value}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedPayments.map((paymentType) => (
|
||||||
|
<div key={paymentType} className="mb-2 border p-2 rounded relative">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleRemovePayment(paymentType)}
|
||||||
|
className="absolute top-0 right-0 bg-red-500 text-white px-2 py-1 rounded-full text-xs"
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="mb-2">
|
||||||
|
<label className="input input-bordered input-secondary flex items-center gap-2">
|
||||||
|
{options.payment_type[paymentType]} - Range Bulan
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
value={paymentDetails[paymentType].range}
|
||||||
|
onChange={(e) => handleRangeChange(paymentType, parseInt(e.target.value))}
|
||||||
|
className="grow border-none focus:ring-0"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-2">
|
||||||
|
<label className="input input-bordered input-secondary flex items-center gap-2">
|
||||||
|
Nominal
|
||||||
|
<input type="text" value={paymentDetails[paymentType].nominal} readOnly className="grow border-none focus:ring-0 bg-gray-100" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-2">
|
||||||
|
<label className="input input-bordered input-secondary flex items-center gap-2">
|
||||||
|
Penalty
|
||||||
|
<input type="text" value={paymentDetails[paymentType].penalty} readOnly className="grow border-none focus:ring-0 bg-gray-100" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<div className="mb-2">
|
||||||
|
<label className="input input-bordered input-secondary flex items-center gap-2">
|
||||||
|
Total Amount
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={Object.values(paymentDetails).reduce((sum, p) => sum + (p.amount || 0), 0)}
|
||||||
|
readOnly
|
||||||
|
className="grow border-none focus:ring-0 bg-gray-100"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" className="btn btn-secondary w-full mt-3">
|
||||||
{initialData ? "Simpan Perubahan" : "Tambah Data"}
|
{initialData ? "Simpan Perubahan" : "Tambah Data"}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -2,12 +2,12 @@ import React, { useState } from 'react';
|
||||||
import { Head } from '@inertiajs/react';
|
import { Head } from '@inertiajs/react';
|
||||||
import ModalInput from '@/Components/ModalInput';
|
import ModalInput from '@/Components/ModalInput';
|
||||||
|
|
||||||
export default function ManualPayment({ santri, penalty, bill, fields }) {
|
export default function ManualPayment({ santri, penalty, bill, fields, options }) {
|
||||||
const [selectedSantri, setSelectedSantri] = useState(null);
|
const [selectedSantri, setSelectedSantri] = useState(null);
|
||||||
return (
|
return (
|
||||||
<div className='text-red-500'>
|
<div className='text-red-500'>
|
||||||
<Head title="Manual Payment" />
|
<Head title="Manual Payment" />
|
||||||
<ModalInput fields={fields} tableName={['payments', 'detail_payments']} initialData={selectedSantri} onClose={() => setSelectedSantri(null)} />
|
<ModalInput fields={fields} options={options} tableName={['payments', 'detail_payments']} initialData={selectedSantri} onClose={() => setSelectedSantri(null)} />
|
||||||
{santri && santri.length > 0 ? santri.map((item, i) => (
|
{santri && santri.length > 0 ? santri.map((item, i) => (
|
||||||
<div key={i} className="p-4 border-b">
|
<div key={i} className="p-4 border-b">
|
||||||
<p>Nis: {item.user.nis}</p>
|
<p>Nis: {item.user.nis}</p>
|
||||||
|
|
Loading…
Reference in New Issue