payment phase 2
|
|
@ -37,10 +37,9 @@ public function indexManualPayment(cekDenda $cekDenda, GenerateMonthlyBill $gene
|
||||||
'penalty' => $penalty,
|
'penalty' => $penalty,
|
||||||
'bill' => $bill,
|
'bill' => $bill,
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'nis' => 'text',
|
'nis' => ['type' => 'text', 'readonly' => true],
|
||||||
'nama' => 'text',
|
'nama' => ['type' => 'text', 'readonly' => true],
|
||||||
'status_santri' => 'text',
|
'status_santri' => ['type' => 'text', 'readonly' => true],
|
||||||
'role_santri' => 'text',
|
|
||||||
],
|
],
|
||||||
'options' => [
|
'options' => [
|
||||||
'payment_type' => $paymentType,
|
'payment_type' => $paymentType,
|
||||||
|
|
@ -54,103 +53,118 @@ public function manualPayment(Request $request, $paymentId)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
DB::beginTransaction();
|
DB::beginTransaction();
|
||||||
DB::enableQueryLog();
|
|
||||||
|
|
||||||
$range = $request->input('range');
|
$range = (int) $request->input('range');
|
||||||
$payment = Payment::find($paymentId);
|
$userId = $request->id;
|
||||||
$userId = User::findOrFail($request->id);
|
$typeId = $request->input('type_id');
|
||||||
|
|
||||||
if (!$payment) {
|
$existingPayment = Payment::where('user_id', $userId)
|
||||||
Payment::create([
|
->where('payment_status', 'pending')
|
||||||
'payment_status' => 'pending',
|
->first();
|
||||||
'amount_payment' => 0,
|
|
||||||
'bank' => null,
|
|
||||||
'no_va' => null,
|
|
||||||
'expired_at' => null,
|
|
||||||
'user_id' => $userId
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$unpaidDetails = DetailPayment::where('payment_id', $paymentId)
|
$unpaidDetails = $existingPayment
|
||||||
|
? DetailPayment::where('payment_id', $existingPayment->id)
|
||||||
->where('status', 'unpaid')
|
->where('status', 'unpaid')
|
||||||
->orderBy('payment_year', 'asc')
|
->orderBy('payment_year')
|
||||||
->orderBy('payment_month', 'asc')
|
->orderBy('payment_month')
|
||||||
->get();
|
->get()
|
||||||
|
: collect();
|
||||||
|
|
||||||
$jumlahUnpaid = $unpaidDetails->count();
|
if ($existingPayment && $unpaidDetails->count() > 0) {
|
||||||
$totalAmount = 0;
|
$totalAmount = 0;
|
||||||
|
$toPay = $unpaidDetails->take($range);
|
||||||
|
$jumlahUnpaid = $toPay->count();
|
||||||
|
|
||||||
Log::info("Jumlah Unpaid: $jumlahUnpaid | Range: $range");
|
foreach ($toPay as $detail) {
|
||||||
|
|
||||||
if ($jumlahUnpaid > 0) {
|
|
||||||
foreach ($unpaidDetails->take($range) as $detail) {
|
|
||||||
$nominal = PaymentType::where('id', $detail->type_id)->value('nominal') ?? 0;
|
$nominal = PaymentType::where('id', $detail->type_id)->value('nominal') ?? 0;
|
||||||
$penalty = $detail->penalty ?? 0;
|
$penalty = $detail->penalty ?? 0;
|
||||||
$total = $nominal + $penalty;
|
$total = $nominal + $penalty;
|
||||||
|
|
||||||
Log::info("Update DetailPayment ID {$detail->id} | Total: $total");
|
|
||||||
|
|
||||||
$detail->update([
|
$detail->update([
|
||||||
'status' => 'paid',
|
'status' => 'paid',
|
||||||
'amount' => $total,
|
'amount' => $nominal,
|
||||||
'penalty' => $penalty,
|
'penalty' => $penalty,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$totalAmount += $total;
|
$totalAmount += $total;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$sisa = max(0, $range - $jumlahUnpaid);
|
$sisa = $range - $jumlahUnpaid;
|
||||||
dd("Sisa pembayaran baru yang perlu dibuat: $sisa");
|
$bulan = $unpaidDetails->last()?->payment_month ?? now()->month;
|
||||||
|
$tahun = $unpaidDetails->last()?->payment_year ?? now()->year;
|
||||||
|
|
||||||
if ($sisa > 0) {
|
for ($i = 0; $i < $sisa; $i++) {
|
||||||
$latestUnpaid = $unpaidDetails->last();
|
$bulan++;
|
||||||
$bulanTerakhir = $latestUnpaid ? $latestUnpaid->payment_month : now()->month;
|
if ($bulan > 12) {
|
||||||
$tahunTerakhir = $latestUnpaid ? $latestUnpaid->payment_year : now()->year;
|
$bulan = 1;
|
||||||
$typeId = $latestUnpaid ? $latestUnpaid->type_id : PaymentType::first()->id;
|
$tahun++;
|
||||||
$nominal = PaymentType::where('id', $typeId)->value('nominal') ?? 0;
|
|
||||||
|
|
||||||
for ($i = 1; $i <= $sisa; $i++) {
|
|
||||||
$bulanTerakhir++;
|
|
||||||
if ($bulanTerakhir > 12) {
|
|
||||||
$bulanTerakhir = 1;
|
|
||||||
$tahunTerakhir++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$totalAmount += $nominal;
|
$nominal = PaymentType::where('id', $typeId)->value('nominal') ?? 0;
|
||||||
|
|
||||||
Log::info("Buat DetailPayment baru untuk bulan: $bulanTerakhir, tahun: $tahunTerakhir");
|
|
||||||
|
|
||||||
DetailPayment::create([
|
DetailPayment::create([
|
||||||
'payment_id' => $paymentId,
|
'payment_id' => $existingPayment->id,
|
||||||
'payment_month' => $bulanTerakhir,
|
'payment_month' => $bulan,
|
||||||
'payment_year' => $tahunTerakhir,
|
'payment_year' => $tahun,
|
||||||
'amount' => $nominal,
|
'amount' => $nominal,
|
||||||
'penalty' => 0,
|
'penalty' => 0,
|
||||||
'status' => 'paid',
|
'status' => 'paid',
|
||||||
'type_id' => $typeId,
|
'type_id' => $typeId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$totalAmount += $nominal;
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingPayment->update([
|
||||||
|
'amount_payment' => DetailPayment::where('payment_id', $existingPayment->id)->sum('amount'),
|
||||||
|
'payment_status' => DetailPayment::where('payment_id', $existingPayment->id)->where('status', 'unpaid')->exists() ? 'pending' : 'success',
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB::commit();
|
||||||
|
return redirect()->back()->with('success', 'Pembayaran berhasil menggunakan data existing');
|
||||||
|
}
|
||||||
|
|
||||||
|
$newPayment = Payment::create([
|
||||||
|
'payment_status' => 'success',
|
||||||
|
'amount_payment' => 0,
|
||||||
|
'bank' => null,
|
||||||
|
'no_va' => null,
|
||||||
|
'expired_at' => null,
|
||||||
|
'user_id' => $userId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$bulan = now()->month;
|
||||||
|
$tahun = now()->year;
|
||||||
|
$nominal = PaymentType::where('id', $typeId)->value('nominal') ?? 0;
|
||||||
|
$totalAmount = 0;
|
||||||
|
|
||||||
|
for ($i = 0; $i < $range; $i++) {
|
||||||
|
DetailPayment::create([
|
||||||
|
'payment_id' => $newPayment->id,
|
||||||
|
'payment_month' => $bulan,
|
||||||
|
'payment_year' => $tahun,
|
||||||
|
'amount' => $nominal,
|
||||||
|
'penalty' => 0,
|
||||||
|
'status' => 'paid',
|
||||||
|
'type_id' => $typeId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$totalAmount += $nominal;
|
||||||
|
|
||||||
|
$bulan++;
|
||||||
|
if ($bulan > 12) {
|
||||||
|
$bulan = 1;
|
||||||
|
$tahun++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$totalAmountFinal = DetailPayment::where('payment_id', $paymentId)->sum('amount');
|
$newPayment->update([
|
||||||
|
'amount_payment' => $totalAmount,
|
||||||
Log::info("Total pembayaran akhir: $totalAmountFinal");
|
|
||||||
|
|
||||||
$payment->update([
|
|
||||||
'amount_payment' => $totalAmountFinal,
|
|
||||||
'payment_status' => DetailPayment::where('payment_id', $paymentId)
|
|
||||||
->where('status', 'unpaid')
|
|
||||||
->exists() ? 'pending' : 'success'
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Log::info("Update Payment ID $paymentId | amount_payment: $totalAmountFinal");
|
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
dd($newPayment);
|
||||||
// dd($request->all());
|
return redirect()->back()->with('success', 'Pembayaran baru berhasil dibuat');
|
||||||
|
|
||||||
return redirect()->back()->with('success', 'Berhasil Melakukan Pembayaran');
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
DB::rollBack();
|
DB::rollBack();
|
||||||
return dd('Error:', $e->getMessage());
|
return dd('Error:', $e->getMessage());
|
||||||
|
|
|
||||||
|
|
@ -3,61 +3,74 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Requests\ProfileUpdateRequest;
|
use App\Http\Requests\ProfileUpdateRequest;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Facades\Redirect;
|
use Illuminate\Support\Facades\Redirect;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use Inertia\Response;
|
use Inertia\Response;
|
||||||
|
|
||||||
class ProfileController extends Controller
|
class ProfileController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Display the user's profile form.
|
|
||||||
*/
|
|
||||||
public function edit(Request $request): Response
|
public function edit(Request $request): Response
|
||||||
{
|
{
|
||||||
return Inertia::render('Profile/Edit', [
|
// $user = User::all();
|
||||||
'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail,
|
return Inertia::render('Profile/Profile');
|
||||||
'status' => session('status'),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
public function updateProfile(Request $request, $id)
|
||||||
/**
|
|
||||||
* Update the user's profile information.
|
|
||||||
*/
|
|
||||||
public function update(ProfileUpdateRequest $request): RedirectResponse
|
|
||||||
{
|
|
||||||
$request->user()->fill($request->validated());
|
|
||||||
|
|
||||||
if ($request->user()->isDirty('email')) {
|
|
||||||
$request->user()->email_verified_at = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$request->user()->save();
|
|
||||||
|
|
||||||
return Redirect::route('profile.edit');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the user's account.
|
|
||||||
*/
|
|
||||||
public function destroy(Request $request): RedirectResponse
|
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'password' => ['required', 'current_password'],
|
'password' => 'nullable',
|
||||||
|
'nama' => 'required',
|
||||||
|
'alamat' => 'required',
|
||||||
|
'jk' => 'required',
|
||||||
|
'tanggal_lahir' => 'required|date',
|
||||||
|
'foto' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
|
||||||
|
], [
|
||||||
|
'nama.required' => 'wajib mengisi nama santri',
|
||||||
|
'alamat.required' => 'wajib mengisi alamat santri',
|
||||||
|
'jk.required' => 'wajib mengisi gender',
|
||||||
|
'tanggal_lahir.required' => 'wajib mengisi tanggal lahir santri',
|
||||||
|
'tanggal_lahir.date' => 'tanggal lahir harus dalam format tanggal yang benar',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$user = $request->user();
|
$santri = User::findOrFail($id);
|
||||||
|
try {
|
||||||
|
|
||||||
Auth::logout();
|
$updateData = [
|
||||||
|
'nama' => $request->nama,
|
||||||
|
'alamat' => $request->alamat,
|
||||||
|
'jk' => $request->jk,
|
||||||
|
'tanggal_lahir' => $request->tanggal_lahir,
|
||||||
|
];
|
||||||
|
|
||||||
$user->delete();
|
if ($request->hasFile('foto')) {
|
||||||
|
if ($santri->foto && File::exists(public_path($santri->foto))) {
|
||||||
|
File::delete(public_path($santri->foto));
|
||||||
|
}
|
||||||
|
|
||||||
$request->session()->invalidate();
|
$foto = $request->file('foto');
|
||||||
$request->session()->regenerateToken();
|
$fotoName = time() . '_' . $foto->getClientOriginalName();
|
||||||
|
$foto->move(public_path('fotoSantri'), $fotoName);
|
||||||
|
$updateData['foto'] = 'fotoSantri/' . $fotoName;
|
||||||
|
}
|
||||||
|
if ($request->filled('password')) {
|
||||||
|
$updateData['password'] = Hash::make($request->password);
|
||||||
|
}
|
||||||
|
|
||||||
return Redirect::to('/');
|
// dd($updateData);
|
||||||
|
// return $updateData;
|
||||||
|
|
||||||
|
$santri->update($updateData);
|
||||||
|
|
||||||
|
return redirect()->back()->with('success', 'Data Berhasil Diubah');
|
||||||
|
} catch (\Throwable $th) {
|
||||||
|
return redirect()->back()->with('error', 'Gagal memperbarui data: ' . $th->getMessage());
|
||||||
|
// return $th->getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,12 @@ public function index()
|
||||||
'nis' => 'text',
|
'nis' => 'text',
|
||||||
'password' => 'password',
|
'password' => 'password',
|
||||||
'alamat' => 'text',
|
'alamat' => 'text',
|
||||||
|
'no_telp' => 'text',
|
||||||
'status_santri' => 'select',
|
'status_santri' => 'select',
|
||||||
'role_santri' => 'select',
|
|
||||||
'jk' => 'select',
|
'jk' => 'select',
|
||||||
'level' => 'select',
|
'level' => 'select',
|
||||||
'tanggal_lahir' => 'date',
|
'tanggal_lahir' => 'date',
|
||||||
'foto' => 'file'
|
'foto' => 'file',
|
||||||
],
|
],
|
||||||
'options' => [
|
'options' => [
|
||||||
'status_santri' => ['lulus' => 'Lulus', 'aktif' => 'Aktif'],
|
'status_santri' => ['lulus' => 'Lulus', 'aktif' => 'Aktif'],
|
||||||
|
|
@ -46,9 +46,9 @@ public function store(Request $request)
|
||||||
'nama' => 'required',
|
'nama' => 'required',
|
||||||
'alamat' => 'required',
|
'alamat' => 'required',
|
||||||
'status_santri' => 'required',
|
'status_santri' => 'required',
|
||||||
'role_santri' => 'required',
|
|
||||||
'jk' => 'required',
|
'jk' => 'required',
|
||||||
'tanggal_lahir' => 'required|date',
|
'tanggal_lahir' => 'required|date',
|
||||||
|
'no_telp' => 'required',
|
||||||
|
|
||||||
'foto' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
|
'foto' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
|
||||||
], [
|
], [
|
||||||
|
|
@ -58,10 +58,10 @@ public function store(Request $request)
|
||||||
'nama.required' => 'wajib mengisi nama santri',
|
'nama.required' => 'wajib mengisi nama santri',
|
||||||
'alamat.required' => 'wajib mengisi alamat santri',
|
'alamat.required' => 'wajib mengisi alamat santri',
|
||||||
'status_santri.required' => 'wajib mengisi status santri',
|
'status_santri.required' => 'wajib mengisi status santri',
|
||||||
'role.required' => 'wajib mengisi role santri',
|
'jk.required' => 'wajib mengisi gender',
|
||||||
'gender.required' => 'wajib mengisi gender',
|
'no_telp' => 'wajib mengisi no telp',
|
||||||
'ttl.required' => 'wajib mengisi tanggal lahir santri',
|
'tanggal_lahir.required' => 'wajib mengisi tanggal lahir santri',
|
||||||
'ttl.date' => 'tanggal lahir harus dalam format tanggal yang benar',
|
'tanggal_lahir.date' => 'tanggal lahir harus dalam format tanggal yang benar',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$fotoPath = null;
|
$fotoPath = null;
|
||||||
|
|
@ -82,13 +82,13 @@ public function store(Request $request)
|
||||||
'nama' => $request->nama,
|
'nama' => $request->nama,
|
||||||
'alamat' => $request->alamat,
|
'alamat' => $request->alamat,
|
||||||
'status_santri' => $request->status_santri,
|
'status_santri' => $request->status_santri,
|
||||||
'role_santri' => $request->role_santri,
|
|
||||||
'jk' => $request->jk,
|
'jk' => $request->jk,
|
||||||
'tanggal_lahir' => $request->tanggal_lahir,
|
'tanggal_lahir' => $request->tanggal_lahir,
|
||||||
|
'no_telp' => $request->no_telp,
|
||||||
'foto' => $fotoPath
|
'foto' => $fotoPath
|
||||||
]);
|
]);
|
||||||
// dd($santri);
|
|
||||||
|
|
||||||
|
// dd($santri);
|
||||||
return redirect()->back()->with('success', 'Data berhasil ditambahkan');
|
return redirect()->back()->with('success', 'Data berhasil ditambahkan');
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
// dd($th->getMessage());
|
// dd($th->getMessage());
|
||||||
|
|
@ -105,7 +105,6 @@ public function update(Request $request, $id)
|
||||||
'nama' => 'required',
|
'nama' => 'required',
|
||||||
'alamat' => 'required',
|
'alamat' => 'required',
|
||||||
'status_santri' => 'required',
|
'status_santri' => 'required',
|
||||||
'role_santri' => 'required',
|
|
||||||
'jk' => 'required',
|
'jk' => 'required',
|
||||||
'tanggal_lahir' => 'required|date',
|
'tanggal_lahir' => 'required|date',
|
||||||
'foto' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
|
'foto' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
|
||||||
|
|
@ -115,10 +114,9 @@ public function update(Request $request, $id)
|
||||||
'nama.required' => 'wajib mengisi nama santri',
|
'nama.required' => 'wajib mengisi nama santri',
|
||||||
'alamat.required' => 'wajib mengisi alamat santri',
|
'alamat.required' => 'wajib mengisi alamat santri',
|
||||||
'status_santri.required' => 'wajib mengisi status santri',
|
'status_santri.required' => 'wajib mengisi status santri',
|
||||||
'role.required' => 'wajib mengisi role santri',
|
'jk.required' => 'wajib mengisi gender',
|
||||||
'gender.required' => 'wajib mengisi gender',
|
'tanggal_lahir.required' => 'wajib mengisi tanggal lahir santri',
|
||||||
'ttl.required' => 'wajib mengisi tanggal lahir santri',
|
'tanggal_lahir.date' => 'tanggal lahir harus dalam format tanggal yang benar',
|
||||||
'ttl.date' => 'tanggal lahir harus dalam format tanggal yang benar',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -129,7 +127,6 @@ public function update(Request $request, $id)
|
||||||
'nama' => $request->nama,
|
'nama' => $request->nama,
|
||||||
'alamat' => $request->alamat,
|
'alamat' => $request->alamat,
|
||||||
'status_santri' => $request->status_santri,
|
'status_santri' => $request->status_santri,
|
||||||
'role_santri' => $request->role_santri,
|
|
||||||
'jk' => $request->jk,
|
'jk' => $request->jk,
|
||||||
'tanggal_lahir' => $request->tanggal_lahir,
|
'tanggal_lahir' => $request->tanggal_lahir,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -2,64 +2,17 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Wallet;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
class WalletController extends Controller
|
class WalletController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
public function walletUser()
|
||||||
* Display a listing of the resource.
|
|
||||||
*/
|
|
||||||
public function index()
|
|
||||||
{
|
{
|
||||||
//
|
$wallet = User::with('wallet')->get();
|
||||||
}
|
// dd($wallet);
|
||||||
|
|
||||||
/**
|
return Inertia::render('list-admin/payment/WalletUser', compact('wallet'));
|
||||||
* Show the form for creating a new resource.
|
|
||||||
*/
|
|
||||||
public function create()
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*/
|
|
||||||
public function store(Request $request)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified resource.
|
|
||||||
*/
|
|
||||||
public function show(Wallet $wallet)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the form for editing the specified resource.
|
|
||||||
*/
|
|
||||||
public function edit(Wallet $wallet)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*/
|
|
||||||
public function update(Request $request, Wallet $wallet)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*/
|
|
||||||
public function destroy(Wallet $wallet)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ public function up(): void
|
||||||
$table->string('level');
|
$table->string('level');
|
||||||
$table->string('nama');
|
$table->string('nama');
|
||||||
$table->string('alamat');
|
$table->string('alamat');
|
||||||
|
$table->bigInteger('no_telp');
|
||||||
$table->enum('status_santri', ['lulus', 'aktif']);
|
$table->enum('status_santri', ['lulus', 'aktif']);
|
||||||
$table->enum('role_santri', ['santri', 'pengurus']);
|
|
||||||
$table->enum('jk', ['laki laki', 'perempuan']);
|
$table->enum('jk', ['laki laki', 'perempuan']);
|
||||||
$table->date('tanggal_lahir');
|
$table->date('tanggal_lahir');
|
||||||
$table->string('foto')->nullable();
|
$table->string('foto')->nullable();
|
||||||
|
|
|
||||||
|
|
@ -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('nominal');
|
$table->decimal('nominal');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ public function up(): void
|
||||||
Schema::create('wallets', function (Blueprint $table) {
|
Schema::create('wallets', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
|
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
|
||||||
$table->float('saldo');
|
$table->decimal('saldo');
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('wallet_transactions', function (Blueprint $table) {
|
Schema::create('wallet_transactions', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
|
$table->string('order_id')->unique();
|
||||||
$table->foreignId('wallet_id')->constrained('wallets')->onDelete('cascade');
|
$table->foreignId('wallet_id')->constrained('wallets')->onDelete('cascade');
|
||||||
$table->enum('transaction_type', ['topup', 'payment']);
|
$table->enum('transaction_type', ['topup', 'payment']);
|
||||||
$table->float('amount');
|
$table->decimal('amount');
|
||||||
$table->string('description');
|
$table->string('description');
|
||||||
$table->enum('status', ['pending', 'success', 'failed']);
|
$table->enum('status', ['pending', 'success', 'failed']);
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,9 @@ public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('payments', function (Blueprint $table) {
|
Schema::create('payments', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
|
$table->string('order_id')->unique()->nullable();
|
||||||
$table->enum('payment_status', ['pending', 'failed', 'success']);
|
$table->enum('payment_status', ['pending', 'failed', 'success']);
|
||||||
$table->float('amount_payment')->nullable();
|
$table->decimal('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();
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ public function up(): void
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->foreignId('payment_id')->constrained('payments')->onDelete('cascade');
|
$table->foreignId('payment_id')->constrained('payments')->onDelete('cascade');
|
||||||
$table->enum('status', ['paid', 'unpaid']);
|
$table->enum('status', ['paid', 'unpaid']);
|
||||||
$table->float('amount')->nullable();
|
$table->decimal('amount')->nullable();
|
||||||
$table->float('penalty')->nullable();
|
$table->decimal('penalty')->nullable();
|
||||||
$table->integer('payment_month');
|
$table->integer('payment_month');
|
||||||
$table->integer('payment_year');
|
$table->integer('payment_year');
|
||||||
$table->foreignId('type_id')->constrained('payment_types')->onDelete('cascade');
|
$table->foreignId('type_id')->constrained('payment_types')->onDelete('cascade');
|
||||||
|
|
|
||||||
|
|
@ -22,22 +22,22 @@ public function run(): void
|
||||||
'nama' => 'Admin',
|
'nama' => 'Admin',
|
||||||
'alamat' => 'Jl. MH. Thamrin No. 10, Ajong',
|
'alamat' => 'Jl. MH. Thamrin No. 10, Ajong',
|
||||||
'status_santri' => 'aktif',
|
'status_santri' => 'aktif',
|
||||||
'role_santri' => 'santri',
|
|
||||||
'jk' => 'laki laki',
|
'jk' => 'laki laki',
|
||||||
'tanggal_lahir' => '2005-08-15',
|
'tanggal_lahir' => '2005-08-15',
|
||||||
'foto' => null
|
'foto' => null,
|
||||||
|
'no_telp' => '80989080980'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'nis' => '0987654321',
|
'nis' => '0987654321',
|
||||||
'password' => Hash::make('pitik123'),
|
'password' => Hash::make('pitik123'),
|
||||||
'level' => 1,
|
'level' => 2,
|
||||||
'nama' => 'Ahmad Kasim',
|
'nama' => 'Ahmad Kasim',
|
||||||
'alamat' => 'Jl. Pesantren No. 10, Jakarta',
|
'alamat' => 'Jl. Pesantren No. 10, Jakarta',
|
||||||
'status_santri' => 'aktif',
|
'status_santri' => 'aktif',
|
||||||
'role_santri' => 'santri',
|
|
||||||
'jk' => 'laki laki',
|
'jk' => 'laki laki',
|
||||||
'tanggal_lahir' => '2003-08-15',
|
'tanggal_lahir' => '2003-08-15',
|
||||||
'foto' => null
|
'foto' => null,
|
||||||
|
'no_telp' => '80989080980'
|
||||||
]
|
]
|
||||||
],);
|
],);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 411 B |
|
Before Width: | Height: | Size: 979 B |
|
Before Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 241 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 758 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 758 KiB |
|
After Width: | Height: | Size: 758 KiB |
|
After Width: | Height: | Size: 758 KiB |
|
After Width: | Height: | Size: 758 KiB |
|
After Width: | Height: | Size: 758 KiB |
|
|
@ -8,7 +8,7 @@ import SunIcon from '@heroicons/react/24/outline/SunIcon'
|
||||||
import { openRightDrawer } from './features/common/rightDrawerSlice'
|
import { openRightDrawer } from './features/common/rightDrawerSlice'
|
||||||
import { RIGHT_DRAWER_TYPES } from '../../../public/utils/globalConstantUtil'
|
import { RIGHT_DRAWER_TYPES } from '../../../public/utils/globalConstantUtil'
|
||||||
import { Link } from '@inertiajs/react'
|
import { Link } from '@inertiajs/react'
|
||||||
import { useForm } from '@inertiajs/react'
|
import { useForm, usePage } from '@inertiajs/react'
|
||||||
|
|
||||||
function Header() {
|
function Header() {
|
||||||
const dispatch = useDispatch()
|
const dispatch = useDispatch()
|
||||||
|
|
@ -18,6 +18,8 @@ function Header() {
|
||||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? "dark" : "light")
|
(window.matchMedia('(prefers-color-scheme: dark)').matches ? "dark" : "light")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { auth } = usePage().props
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
themeChange(false);
|
themeChange(false);
|
||||||
document.documentElement.setAttribute("data-theme", theme);
|
document.documentElement.setAttribute("data-theme", theme);
|
||||||
|
|
@ -62,11 +64,13 @@ function Header() {
|
||||||
<div className="dropdown dropdown-end ml-4">
|
<div className="dropdown dropdown-end ml-4">
|
||||||
<label tabIndex={0} className="btn btn-ghost btn-circle avatar">
|
<label tabIndex={0} className="btn btn-ghost btn-circle avatar">
|
||||||
<div className="w-10 rounded-full">
|
<div className="w-10 rounded-full">
|
||||||
<img src={"/public/fotoSantri/no-pic.png"} alt="profile" />
|
<img src={auth.user.foto ? `${auth.user.foto}` : `/fotoSantri/no-pic.png`} alt="profile" />
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<ul tabIndex={0} className="menu menu-compact dropdown-content mt-3 p-2 shadow bg-base-100 rounded-box w-52">
|
<ul tabIndex={0} className="menu menu-compact dropdown-content mt-3 p-2 shadow bg-base-100 rounded-box w-52">
|
||||||
<li><Link href="/app/settings-profile">Profile Settings</Link></li>
|
<li className="ml-3" >Welcome, {auth.user.nama}</li>
|
||||||
|
<div className="divider mt-0 mb-0"></div>
|
||||||
|
<li><Link href={route('profile.edit')}>Profile Settings</Link></li>
|
||||||
<li><Link href="/app/settings-billing">Bill History</Link></li>
|
<li><Link href="/app/settings-billing">Bill History</Link></li>
|
||||||
<div className="divider mt-0 mb-0"></div>
|
<div className="divider mt-0 mb-0"></div>
|
||||||
<li><a href="#" onClick={logoutUser}>Logout</a></li>
|
<li><a href="#" onClick={logoutUser}>Logout</a></li>
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,15 @@ function LeftSidebar() {
|
||||||
return (
|
return (
|
||||||
<div className="drawer-side z-30">
|
<div className="drawer-side z-30">
|
||||||
<label htmlFor="left-sidebar-drawer" className="drawer-overlay"></label>
|
<label htmlFor="left-sidebar-drawer" className="drawer-overlay"></label>
|
||||||
<ul className="menu pt-2 w-80 bg-base-100 min-h-full text-base-content">
|
<ul className="menu pt-2 w-64 bg-base-100 min-h-full text-base-content">
|
||||||
<button
|
<button
|
||||||
className="btn btn-ghost bg-base-300 btn-circle z-50 top-0 right-0 mt-4 mr-2 absolute lg:hidden"
|
className="btn btn-ghost bg-base-300 btn-circle z-50 top-0 right-0 mt-4 mr-2 absolute lg:hidden"
|
||||||
onClick={close}
|
onClick={close}
|
||||||
>
|
>
|
||||||
<XMarkIcon className="h-5 w-5" />
|
<XMarkIcon className="h-5 w-5" />
|
||||||
</button>
|
</button>
|
||||||
<li className="mb-2 font-semibold text-xl">
|
<li className="mb-2 font-semibold">
|
||||||
<Link href="/dashboard">GoGoSantri</Link>
|
<Link href="/dashboard" className='flex justify-center'><img src="/assets/gogoSantri.png" alt="pp" width={100} /></Link>
|
||||||
</li>
|
</li>
|
||||||
{routes.map((route, k) => (
|
{routes.map((route, k) => (
|
||||||
<li key={k}>
|
<li key={k}>
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ const ModalInput = ({ fields, tableName, options, initialData, onClose, showPaym
|
||||||
if (!selectedPayments.includes(value)) {
|
if (!selectedPayments.includes(value)) {
|
||||||
setSelectedPayments([...selectedPayments, value]);
|
setSelectedPayments([...selectedPayments, value]);
|
||||||
|
|
||||||
const nominal = options.payment_nominal?.[value] || 0;
|
const nominal = parseInt(options.payment_nominal?.[value]) || 0;
|
||||||
const penalty = options.payment_penalty?.[value] || 0;
|
const penalty = parseInt(options.payment_penalty?.[value]) || 0;
|
||||||
|
|
||||||
setPaymentDetails({
|
setPaymentDetails({
|
||||||
...paymentDetails,
|
...paymentDetails,
|
||||||
|
|
@ -69,177 +69,203 @@ const ModalInput = ({ fields, tableName, options, initialData, onClose, showPaym
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
// console.log("tableName:", tableName);
|
|
||||||
|
|
||||||
const formDataObj = new FormData()
|
const formDataObj = new FormData();
|
||||||
Object.keys(formData).forEach((key) => {
|
Object.keys(formData).forEach((key) => {
|
||||||
if (key === 'foto' && !(formData[key] instanceof File)) {
|
if (key === 'foto' && !(formData[key] instanceof File)) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
formDataObj.append(key, formData[key])
|
formDataObj.append(key, formData[key]);
|
||||||
})
|
});
|
||||||
|
|
||||||
if (initialData) {
|
if (showPayments) {
|
||||||
Inertia.post(`/update${tableName}/${initialData.id}`, formDataObj, {
|
formDataObj.append('payment_details', JSON.stringify(paymentDetails));
|
||||||
forceFormData: true,
|
|
||||||
onError: (errors) => setErrors(errors),
|
|
||||||
onSuccess: () => {
|
|
||||||
document.getElementById('modal_input').checked = false
|
|
||||||
setFormData({})
|
|
||||||
setErrors({})
|
|
||||||
onClose({})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Inertia.post(`/add${tableName}`, formDataObj, {
|
|
||||||
forceFormData: true,
|
|
||||||
onError: (errors) => setErrors(errors),
|
|
||||||
onSuccess: () => {
|
|
||||||
document.getElementById('modal_input').checked = false
|
|
||||||
setFormData({})
|
|
||||||
setErrors({})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
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({});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<input type="checkbox" id="modal_input" className="modal-toggle" />
|
<input type="checkbox" id="modal_input" className="modal-toggle" />
|
||||||
<div className="modal" role="dialog">
|
<div className="modal" role="dialog">
|
||||||
<div className="modal-box">
|
<div className="modal-box max-w-lg">
|
||||||
<h2 className="font-bold text-lg text-center mb-5">
|
<h2 className="font-bold text-xl text-center mb-6 border-b pb-2">
|
||||||
{initialData ? "Edit Data" : "Tambah Data"}
|
Form Data
|
||||||
</h2>
|
</h2>
|
||||||
<form onSubmit={handleSubmit} encType="multipart/form-data">
|
<form onSubmit={handleSubmit} encType="multipart/form-data" className="space-y-4">
|
||||||
{Object.keys(fields).map((field) => (
|
{Object.entries(fields).map(([field, config]) => {
|
||||||
<div key={field} className="mb-2">
|
const type = typeof config === "string" ? config : config.type;
|
||||||
<label className="input input-bordered input-secondary flex items-center gap-2">
|
const readOnly = typeof config === "object" && config.readonly;
|
||||||
{field.replace("_", " ")}
|
return (
|
||||||
{fields[field] === "select" ? (
|
<div key={field} className="form-control">
|
||||||
|
<label className="label font-semibold capitalize">{field.replace("_", " ")}</label>
|
||||||
|
{type === "select" ? (
|
||||||
<select
|
<select
|
||||||
name={field}
|
name={field}
|
||||||
value={formData[field] || ""}
|
value={formData[field] || ""}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="select select-bordered w-full select-secondary"
|
className="select select-info focus:outline-none focus:ring-1 ring-info w-full"
|
||||||
>
|
>
|
||||||
<option disabled value="">
|
<option disabled value="">
|
||||||
Pilih {field.replace("_", " ")}
|
Pilih {field.replace("_", " ")}
|
||||||
</option>
|
</option>
|
||||||
{options[field] && Object.entries(options[field]).map(([key, value]) => (
|
{options[field] &&
|
||||||
<option key={key} value={key}>{value}</option>
|
Object.entries(options[field]).map(([key, value]) => (
|
||||||
))}
|
<option key={key} value={key}>
|
||||||
|
{value}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
</select>
|
</select>
|
||||||
) : fields[field] === "file" ? (
|
) : type === "file" ? (
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
name={field}
|
name={field}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className="file-input file-input-bordered w-full file-input-secondary"
|
className="file-input file-input-info focus:outline-none focus:ring-1 ring-info w-full"
|
||||||
/>
|
/>
|
||||||
|
) : 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'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
) : (
|
) : (
|
||||||
fields[field] === "password" ? (
|
<input
|
||||||
<input
|
type={type}
|
||||||
type="password"
|
name={field}
|
||||||
name={field}
|
value={formData[field] || ""}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
value=""
|
readOnly={readOnly}
|
||||||
className="grow border-none focus:ring-0"
|
className="input input-info focus:outline-none focus:ring-1 ring-info w-full"
|
||||||
placeholder="Kosongkan jika tidak ingin mengubah password"
|
placeholder={`Masukkan ${field.replace("_", " ")}`}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<input
|
|
||||||
type={fields[field]}
|
|
||||||
name={field}
|
|
||||||
value={formData[field] || ""}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="grow border-none focus:ring-0"
|
|
||||||
placeholder={`Masukkan ${field.replace("_", " ")}`}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
</label>
|
{errors[field] && (
|
||||||
{errors[field] && <p className="text-red-500 text-sm">{errors[field]}</p>}
|
<p className="text-red-500 text-sm mt-1">{errors[field]}</p>
|
||||||
</div>
|
)}
|
||||||
))}
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
|
||||||
{showPayments && (
|
{showPayments && (
|
||||||
<div>
|
<div className="space-y-4 mt-4">
|
||||||
<div className="mb-2">
|
<div className="form-control">
|
||||||
<label className="input input-bordered input-secondary flex items-center gap-2">
|
<label className="label font-semibold">Payment Type</label>
|
||||||
Payment Type
|
<select
|
||||||
<select onChange={handlePaymentChange} className="select select-bordered w-full select-secondary">
|
onChange={handlePaymentChange}
|
||||||
<option disabled value="">Pilih Payment Type</option>
|
className="select select-info focus:outline-none focus:ring-1 ring-info w-full"
|
||||||
{options.payment_type &&
|
>
|
||||||
Object.entries(options.payment_type).map(([key, value]) => (
|
<option disabled value="">
|
||||||
<option key={key} value={key}>{value}</option>
|
Pilih Payment Type
|
||||||
))}
|
</option>
|
||||||
</select>
|
{options.payment_type &&
|
||||||
</label>
|
Object.entries(options.payment_type).map(([key, value]) => (
|
||||||
|
<option key={key} value={key}>
|
||||||
|
{value}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedPayments.map((paymentType) => (
|
{selectedPayments.map((paymentType) => (
|
||||||
<div key={paymentType} className="mb-2 border p-2 rounded relative">
|
<div
|
||||||
|
key={paymentType}
|
||||||
|
className="border rounded-lg p-4 relative bg-gray-50 shadow-sm"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleRemovePayment(paymentType)}
|
onClick={() => handleRemovePayment(paymentType)}
|
||||||
className="absolute top-0 right-0 bg-red-500 text-white px-2 py-1 rounded-full text-xs"
|
className="absolute top-2 right-2 bg-red-500 hover:bg-red-600 text-white px-2 py-1 rounded-full text-xs"
|
||||||
>
|
>
|
||||||
X
|
X
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="mb-2">
|
<div className="form-control mb-2">
|
||||||
<label className="input input-bordered input-secondary flex items-center gap-2">
|
<label className="label font-semibold">
|
||||||
{options.payment_type[paymentType]} - Range Bulan
|
{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" name="range"
|
|
||||||
/>
|
|
||||||
</label>
|
</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>
|
||||||
|
|
||||||
<div className="mb-2">
|
<div className="form-control mb-2">
|
||||||
<label className="input input-bordered input-secondary flex items-center gap-2">
|
<label className="label font-semibold">Nominal</label>
|
||||||
Nominal
|
<input
|
||||||
<input type="text" value={paymentDetails[paymentType].nominal} readOnly className="grow border-none focus:ring-0 bg-gray-100" />
|
type="text"
|
||||||
</label>
|
value={paymentDetails[paymentType].nominal}
|
||||||
|
readOnly
|
||||||
|
className="input bg-gray-100 w-full"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-2">
|
<div className="form-control mb-2">
|
||||||
<label className="input input-bordered input-secondary flex items-center gap-2">
|
<label className="label font-semibold">Penalty</label>
|
||||||
Penalty
|
<input
|
||||||
<input type="text" value={paymentDetails[paymentType].penalty} readOnly className="grow border-none focus:ring-0 bg-gray-100" />
|
type="text"
|
||||||
</label>
|
value={paymentDetails[paymentType].penalty}
|
||||||
|
readOnly
|
||||||
|
className="input bg-gray-100 w-full"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</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 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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<button type="submit" className="btn btn-secondary w-full mt-3">
|
<button type="submit" className="btn btn-info text-white font-bold w-full mt-4">
|
||||||
{initialData ? "Simpan Perubahan" : "Tambah Data"}
|
{initialData ? "submit" : "Tambah Data"}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<label className="modal-backdrop" htmlFor="modal_input">Close</label>
|
<label className="modal-backdrop" htmlFor="modal_input">
|
||||||
|
Close
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useForm, usePage } from '@inertiajs/react';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { setPageTitle } from '@/Components/features/common/headerSlice';
|
||||||
|
import { Head } from '@inertiajs/react';
|
||||||
|
|
||||||
|
export default function ProfilePage() {
|
||||||
|
const { auth } = usePage().props;
|
||||||
|
const [preview, setPreview] = useState(null);
|
||||||
|
const id = auth.user.id
|
||||||
|
|
||||||
|
|
||||||
|
const { data, setData, post, processing, errors } = useForm({
|
||||||
|
nama: auth.user.nama || '',
|
||||||
|
alamat: auth.user.alamat || '',
|
||||||
|
no_telp: auth.user.no_telp || '',
|
||||||
|
jk: auth.user.jk || 'laki laki',
|
||||||
|
tanggal_lahir: auth.user.tanggal_lahir || '',
|
||||||
|
password: '',
|
||||||
|
foto: null
|
||||||
|
});
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(setPageTitle("Profile Page"));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
post(route('profile.update', id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePhotoChange = (e) => {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
if (file) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => {
|
||||||
|
setPreview(reader.result);
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
setData('foto', file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full max-w-4xl mx-auto bg-base-100 shadow-sm rounded-lg overflow-hidden">
|
||||||
|
<Head title="Profile Page" />
|
||||||
|
|
||||||
|
<div className="px-6 py-4 flex items-center justify-between">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="relative h-16 w-16 rounded-full overflow-hidden mr-4 border border-gray-200 cursor-pointer group">
|
||||||
|
<label htmlFor="photo-upload" className="block relative h-16 w-16 rounded-full overflow-hidden border-4 border-transparent hover:border-blue-500 cursor-pointer">
|
||||||
|
<img
|
||||||
|
src={preview || auth.user.foto || "/fotoSantri/no-pic.png"}
|
||||||
|
alt="Profile"
|
||||||
|
className="h-full w-full object-cover transition duration-200"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center bg-black opacity-0 hover:opacity-30 transition duration-200 rounded-full">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
className="h-8 w-8 text-white"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M12 4.5c-3.315 0-6 2.685-6 6s2.685 6 6 6 6-2.685 6-6-2.685-6-6-6zM12 10.5a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5zm-6 5.25h12c.828 0 1.5.672 1.5 1.5v3c0 .828-.672 1.5-1.5 1.5H6c-.828 0-1.5-.672-1.5-1.5v-3c0-.828.672-1.5 1.5-1.5z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
id="photo-upload"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
className="hidden"
|
||||||
|
onChange={handlePhotoChange}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl font-semibold">{auth.user.nama}</h2>
|
||||||
|
<p className="text-sm">{auth.user.level == 1 ? 'Admin' : ''}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form onSubmit={handleSubmit} className="px-6 py-4" encType='multipart/form-data'>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<Input label="Nama Lengkap" name="nama" value={data.nama} onChange={setData} error={errors.nama} />
|
||||||
|
<Input label="Alamat" name="alamat" value={data.alamat} onChange={setData} error={errors.alamat} />
|
||||||
|
<Input label="No Telepon" name="no_telp" value={data.no_telp} onChange={setData} error={errors.no_telp} />
|
||||||
|
<Input label="Tanggal Lahir" name="tanggal_lahir" type="date" value={data.tanggal_lahir} onChange={setData} error={errors.tanggal_lahir} />
|
||||||
|
<Select label="Jenis Kelamin" name="jk" value={data.jk} onChange={setData} options={['laki laki', 'perempuan']} error={errors.jk} />
|
||||||
|
<Input label="Password" name="password" type="password" value={data.password} onChange={setData} error={errors.password} placeholder="Kosongkan jika tidak ingin mengganti password" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-6 text-right">
|
||||||
|
<button
|
||||||
|
type='submit'
|
||||||
|
className="bg-blue-500 text-white px-4 py-2 rounded-md text-sm font-medium"
|
||||||
|
disabled={processing}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Input({ label, name, value, onChange, type = 'text', error, placeholder = '' }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-1">{label}</label>
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
name={name}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(name, e.target.value)}
|
||||||
|
placeholder={placeholder}
|
||||||
|
className={`w-full px-4 py-2 bg-gray-50 border ${error ? 'border-red-500' : 'border-gray-200'} rounded-md focus:outline-none`}
|
||||||
|
/>
|
||||||
|
{error && <div className="text-red-500 text-sm mt-1">{error}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Select({ label, name, value, onChange, options = [], error }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium mb-1">{label}</label>
|
||||||
|
<select
|
||||||
|
name={name}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(name, e.target.value)}
|
||||||
|
className={`w-full px-4 py-2 bg-gray-50 border ${error ? 'border-red-500' : 'border-gray-200'} rounded-md focus:outline-none`}
|
||||||
|
>
|
||||||
|
{options.map((opt) => (
|
||||||
|
<option key={opt} value={opt}>{opt}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
{error && <div className="text-red-500 text-sm mt-1">{error}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ export default function ManualPayment({ santri, fields, options }) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold">{item.nama}</div>
|
<div className="font-bold">{item.nama}</div>
|
||||||
<div className="text-sm opacity-50">{item.role_santri}</div>
|
<div className="text-sm opacity-50">{item.level == 1 ? 'Admin' : 'User'}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -181,7 +181,7 @@ export default function ManualPayment({ santri, fields, options }) {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{selectedSantri ? selectedPayments.map((payment, idx) =>
|
{selectedPayments.map((payment, idx) =>
|
||||||
payment.detail_payments.map((detail) => (
|
payment.detail_payments.map((detail) => (
|
||||||
<tr key={`${idx}-${detail.id}`}>
|
<tr key={`${idx}-${detail.id}`}>
|
||||||
<td>{detail.payment_type?.payment_type}</td>
|
<td>{detail.payment_type?.payment_type}</td>
|
||||||
|
|
@ -195,7 +195,7 @@ export default function ManualPayment({ santri, fields, options }) {
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
) : 'tidak ada data pembayaran santri'}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import DeleteButton from '@/Components/DeleteButton';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { setPageTitle } from '@/Components/features/common/headerSlice';
|
import { setPageTitle } from '@/Components/features/common/headerSlice';
|
||||||
|
|
||||||
export default function PaymentType({ tableName, paymentType, fields }) {
|
export default function PaymentType({ paymentType, fields }) {
|
||||||
const [selectedPaymentType, setSelectedPaymentType] = useState(null);
|
const [selectedPaymentType, setSelectedPaymentType] = useState(null);
|
||||||
const [isDeleteOpen, setDeleteOpen] = useState(false);
|
const [isDeleteOpen, setDeleteOpen] = useState(false);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
|
@ -27,15 +27,15 @@ export default function PaymentType({ tableName, paymentType, fields }) {
|
||||||
}
|
}
|
||||||
}, [searchTerm, paymentType]);
|
}, [searchTerm, paymentType]);
|
||||||
|
|
||||||
|
const handleSearch = (e) => {
|
||||||
|
setSearchTerm(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
const deleteModal = (item) => {
|
const deleteModal = (item) => {
|
||||||
setSelectedPaymentType(item);
|
setSelectedPaymentType(item);
|
||||||
setDeleteOpen(true);
|
setDeleteOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = (e) => {
|
|
||||||
setSearchTerm(e.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head title="Daftar Payment Type" />
|
<Head title="Daftar Payment Type" />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Head } from '@inertiajs/react';
|
||||||
|
import ModalInput from '@/Components/ModalInput';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { setPageTitle } from '@/Components/features/common/headerSlice';
|
||||||
|
import { CurrencyDollarIcon } from '@heroicons/react/24/outline'
|
||||||
|
|
||||||
|
|
||||||
|
export default function ManualPayment({ wallet }) {
|
||||||
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
const [filteredSantri, setFilteredSantri] = useState([]);
|
||||||
|
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(setPageTitle("Data Dompet Santri"));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
// console.log(santri.id)
|
||||||
|
useEffect(() => {
|
||||||
|
if (wallet) {
|
||||||
|
setFilteredSantri(
|
||||||
|
wallet.filter(item =>
|
||||||
|
item.nama.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||||
|
item.nis.toString().includes(searchTerm)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [searchTerm, wallet]);
|
||||||
|
|
||||||
|
const handleSearch = (e) => {
|
||||||
|
setSearchTerm(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head title="Dompet Santri" />
|
||||||
|
<div className="card bg-base-100 shadow-xl">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="flex items-center mb-6">
|
||||||
|
<div className="bg-gradient-to-tr from-blue-400 to-blue-600 p-3 rounded-lg mr-3">
|
||||||
|
<CurrencyDollarIcon className="h-6 w-6 text-white" />
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold">Data Dompet Santri</h1>
|
||||||
|
<div className="ml-auto">
|
||||||
|
<span className="text-gray-700">Overview</span>
|
||||||
|
<span className="ml-2 text-gray-500">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 inline" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<div className="form-control">
|
||||||
|
<div className="input-group">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Cari Santri..."
|
||||||
|
className="input input-bordered"
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={handleSearch}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="table table-zebra w-full">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nama</th>
|
||||||
|
<th>Nis</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Saldo</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{filteredSantri.length > 0 ? (
|
||||||
|
filteredSantri.map((item, i) => (
|
||||||
|
<tr key={i}>
|
||||||
|
<td>
|
||||||
|
<div className="flex items-center space-x-3">
|
||||||
|
<div className="avatar">
|
||||||
|
<div className="mask mask-squircle w-12 h-12">
|
||||||
|
<img src={`https://ui-avatars.com/api/?name=${item.nama}`} alt="Avatar" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="font-bold">{item.nama}</div>
|
||||||
|
<div className="text-sm opacity-50">{item.level === 1 ? 'Admin' : 'Santri'}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{item.nis}</td>
|
||||||
|
<td>
|
||||||
|
<div className={`badge ${item.status_santri === 'aktif' ? 'badge-success' : 'badge-warning'} gap-2 text-white`}>
|
||||||
|
{item.status_santri || "Open"}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{item.wallet ? item.wallet.saldo : 'Tidak ada saldo'}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan="4" className="text-center">Tidak ada data santri.</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div className="flex justify-center mt-4">
|
||||||
|
{wallet?.links?.map((link, index) => (
|
||||||
|
<button
|
||||||
|
key={index}
|
||||||
|
className={`px-4 py-2 mx-1 ${link.active ? "bg-blue-500 text-white" : "bg-gray-200"
|
||||||
|
}`}
|
||||||
|
onClick={() => {
|
||||||
|
if (link.url) window.location.href = link.url;
|
||||||
|
}}
|
||||||
|
disabled={!link.url}
|
||||||
|
>
|
||||||
|
{link.label.replace('«', '«').replace('»', '»')}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -102,7 +102,7 @@ export default function IndexSantri({ santri, fields, options }) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-bold">{item.nama}</div>
|
<div className="font-bold">{item.nama}</div>
|
||||||
<div className="text-sm opacity-50">{item.role_santri || "Santri"}</div>
|
<div className="text-sm opacity-50">{item.level == 1 ? 'Admin' : 'User'}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ const Dashboard = lazy(() => import('@/Pages/protected/Dashboard'))
|
||||||
const IndexSantri = lazy(() => import('@/Pages/list-admin/santri/IndexSantri'))
|
const IndexSantri = lazy(() => import('@/Pages/list-admin/santri/IndexSantri'))
|
||||||
const PaymentType = lazy(() => import('@/Pages/list-admin/payment/PaymentType'))
|
const PaymentType = lazy(() => import('@/Pages/list-admin/payment/PaymentType'))
|
||||||
const ManualPayment = lazy(() => import('@/Pages/list-admin/payment/ManualPayment'))
|
const ManualPayment = lazy(() => import('@/Pages/list-admin/payment/ManualPayment'))
|
||||||
|
const WalletUser = lazy(() => import('@pages/list-admin/wallet/WalletUser'))
|
||||||
|
|
||||||
console.log(route('dashboard'))
|
console.log(route('dashboard'))
|
||||||
|
|
||||||
|
|
@ -26,6 +27,10 @@ const routes = [
|
||||||
path: route('indexManualPayment'),
|
path: route('indexManualPayment'),
|
||||||
component: ManualPayment,
|
component: ManualPayment,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: route('walletUser'),
|
||||||
|
componenet: WalletUser
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
export default routes
|
export default routes
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,11 @@ const routes = [
|
||||||
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
||||||
name: 'Data Santri'
|
name: 'Data Santri'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/data-payment-type',
|
||||||
|
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
||||||
|
name: 'Tipe Pembayaran'
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
@ -56,16 +61,16 @@ const routes = [
|
||||||
icon: <CurrencyDollarIcon className={`${iconClasses} inline`} />,
|
icon: <CurrencyDollarIcon className={`${iconClasses} inline`} />,
|
||||||
name: 'Data Pembayaran',
|
name: 'Data Pembayaran',
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
|
||||||
path: '/data-payment-type',
|
|
||||||
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
|
||||||
name: 'Tipe Pembayaran'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/index-manual-payment',
|
path: '/index-manual-payment',
|
||||||
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
||||||
name: 'Data Pembayaran'
|
name: 'Data Pembayaran'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/wallet-user',
|
||||||
|
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
||||||
|
name: 'Wallet User'
|
||||||
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
|
||||||
import store from '../js/Auth/store';
|
import store from '../js/Auth/store';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
|
||||||
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
|
const appName = import.meta.env.VITE_APP_NAME || 'GogoSantri';
|
||||||
const Layout = lazy(() => import('@/Components/Layout'));
|
const Layout = lazy(() => import('@/Components/Layout'));
|
||||||
|
|
||||||
createInertiaApp({
|
createInertiaApp({
|
||||||
|
|
@ -16,7 +16,6 @@ createInertiaApp({
|
||||||
resolve: (name) => {
|
resolve: (name) => {
|
||||||
return resolvePageComponent(`./Pages/${name}.jsx`, import.meta.glob('./Pages/**/**.jsx'))
|
return resolvePageComponent(`./Pages/${name}.jsx`, import.meta.glob('./Pages/**/**.jsx'))
|
||||||
.then((page) => {
|
.then((page) => {
|
||||||
// Pastikan semua halaman pakai LayoutWrapper
|
|
||||||
page.default.layout = (page) => <LayoutWrapper>{page}</LayoutWrapper>;
|
page.default.layout = (page) => <LayoutWrapper>{page}</LayoutWrapper>;
|
||||||
return page;
|
return page;
|
||||||
});
|
});
|
||||||
|
|
@ -39,7 +38,7 @@ createInertiaApp({
|
||||||
import { usePage } from '@inertiajs/react';
|
import { usePage } from '@inertiajs/react';
|
||||||
|
|
||||||
const LayoutWrapper = ({ children }) => {
|
const LayoutWrapper = ({ children }) => {
|
||||||
const { url } = usePage(); // Sekarang usePage() ada di dalam Inertia App
|
const { url } = usePage();
|
||||||
const isLoginPage = url.startsWith('/login') || url.startsWith('/auth/login');
|
const isLoginPage = url.startsWith('/login') || url.startsWith('/auth/login');
|
||||||
|
|
||||||
return isLoginPage ? <>{children}</> : <Layout>{children}</Layout>;
|
return isLoginPage ? <>{children}</> : <Layout>{children}</Layout>;
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,25 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
|
|
||||||
<title inertia>{{ config('app.name', 'Laravel') }}</title>
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Fonts -->
|
<title inertia>{{ config('app.name', 'GogoSantri') }}</title>
|
||||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
|
||||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
<!-- Fonts -->
|
||||||
|
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||||
|
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
||||||
|
|
||||||
|
<!-- Scripts -->
|
||||||
|
@routes
|
||||||
|
@viteReactRefresh
|
||||||
|
@vite(['resources/js/app.jsx', "resources/js/Pages/{$page['component']}.jsx"])
|
||||||
|
@inertiaHead
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="font-sans antialiased">
|
||||||
|
@inertia
|
||||||
|
</body>
|
||||||
|
|
||||||
<!-- Scripts -->
|
|
||||||
@routes
|
|
||||||
@viteReactRefresh
|
|
||||||
@vite(['resources/js/app.jsx', "resources/js/Pages/{$page['component']}.jsx"])
|
|
||||||
@inertiaHead
|
|
||||||
</head>
|
|
||||||
<body class="font-sans antialiased">
|
|
||||||
@inertia
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\ProfileController;
|
use App\Http\Controllers\ProfileController;
|
||||||
|
use App\Http\Controllers\WalletController;
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use App\Http\Controllers\SantriController;
|
use App\Http\Controllers\SantriController;
|
||||||
|
|
@ -9,17 +10,6 @@
|
||||||
use App\Http\Controllers\HomeController;
|
use App\Http\Controllers\HomeController;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
/*
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
| Web Routes
|
|
||||||
|--------------------------------------------------------------------------
|
|
||||||
|
|
|
||||||
| Here is where you can register web routes for your application. These
|
|
||||||
| routes are loaded by the RouteServiceProvider within a group which
|
|
||||||
| contains the "web" middleware group. Now create something great!
|
|
||||||
|
|
|
||||||
*/
|
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
return redirect()->route('login');
|
return redirect()->route('login');
|
||||||
});
|
});
|
||||||
|
|
@ -28,17 +18,14 @@
|
||||||
return redirect()->route('login');
|
return redirect()->route('login');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Route::get('/dashboard', function () {
|
|
||||||
// return Inertia::render('Dashboard');
|
|
||||||
// })->middleware(['auth', 'verified'])->name('dashboard');
|
|
||||||
|
|
||||||
Route::middleware('auth')->group(function () {
|
Route::middleware('auth')->group(function () {
|
||||||
|
|
||||||
// dashboard
|
// dashboard
|
||||||
Route::get('/dashboard', [HomeController::class, 'index'])->name('dashboard');
|
Route::get('/dashboard', [HomeController::class, 'index'])->name('dashboard');
|
||||||
|
|
||||||
// profile
|
// profile
|
||||||
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
|
||||||
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
|
Route::post('/update-profile/{id}', [ProfileController::class, 'updateProfile'])->name('profile.update');
|
||||||
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
|
|
||||||
|
|
||||||
// santri
|
// santri
|
||||||
Route::get('/data-santri', [SantriController::class, 'index'])->name('indexSantri');
|
Route::get('/data-santri', [SantriController::class, 'index'])->name('indexSantri');
|
||||||
|
|
@ -56,6 +43,9 @@
|
||||||
Route::get('/index-manual-payment', [PaymentController::class, 'indexManualPayment'])->name('indexManualPayment');
|
Route::get('/index-manual-payment', [PaymentController::class, 'indexManualPayment'])->name('indexManualPayment');
|
||||||
Route::post('/updatepayments/{paymentId}', [PaymentController::class, 'manualPayment'])->name('manualPayment');
|
Route::post('/updatepayments/{paymentId}', [PaymentController::class, 'manualPayment'])->name('manualPayment');
|
||||||
|
|
||||||
|
// wallet
|
||||||
|
Route::get('/wallet-user', [WalletController::class, 'walletUser'])->name('walletUser');
|
||||||
|
|
||||||
Route::get('profile-settings', function () {
|
Route::get('profile-settings', function () {
|
||||||
return Inertia::render('ProfileSettings');
|
return Inertia::render('ProfileSettings');
|
||||||
});
|
});
|
||||||
|
|
|
||||||