final
|
@ -1,65 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\DetailPayment;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DetailPaymentController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(DetailPayment $detailPayment)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(DetailPayment $detailPayment)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, DetailPayment $detailPayment)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(DetailPayment $detailPayment)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
|
@ -4,11 +4,62 @@
|
|||
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\User;
|
||||
use App\Models\Wallet;
|
||||
use App\Models\DetailPayment;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return Inertia::render('Dashboard');
|
||||
$now = Carbon::now();
|
||||
$month = $now->month;
|
||||
$year = $now->year;
|
||||
|
||||
$monthlyIncome = DetailPayment::where('status', 'paid')
|
||||
->where('payment_month', $month)
|
||||
->where('payment_year', $year)
|
||||
->whereHas('payments', fn($q) => $q->where('payment_status', 'success'))
|
||||
->sum(DB::raw('IFNULL(amount,0) + IFNULL(penalty,0)'));
|
||||
|
||||
|
||||
$studentCount = User::where('level', 2)
|
||||
->where('status_santri', 'aktif')
|
||||
->count();
|
||||
|
||||
$totalBalance = Wallet::sum('saldo');
|
||||
|
||||
$paymentTrend = DetailPayment::select('payment_month', 'payment_year')
|
||||
->selectRaw('SUM(IFNULL(amount,0) + IFNULL(penalty,0)) as total')
|
||||
->where('status', 'paid')
|
||||
->whereHas('payments', fn($q) => $q->where('payment_status', 'success'))
|
||||
->whereBetween(DB::raw("STR_TO_DATE(CONCAT(payment_year,'-',payment_month,'-01'), '%Y-%m-%d')"), [
|
||||
now()->subMonths(11)->startOfMonth()->toDateString(),
|
||||
now()->endOfMonth()->toDateString(),
|
||||
])
|
||||
->groupBy('payment_year', 'payment_month')
|
||||
->orderBy('payment_year')
|
||||
->orderBy('payment_month')
|
||||
->get();
|
||||
|
||||
$labels = [];
|
||||
$data = [];
|
||||
|
||||
foreach ($paymentTrend as $pt) {
|
||||
$labels[] = Carbon::createFromDate($pt->payment_year, $pt->payment_month, 1)->format('M Y');
|
||||
$data[] = (int)$pt->total;
|
||||
}
|
||||
|
||||
return Inertia()->render('Dashboard', [
|
||||
'monthlyIncome' => $monthlyIncome,
|
||||
'studentCount' => $studentCount,
|
||||
'totalBalance' => $totalBalance,
|
||||
'paymentTrend' => [
|
||||
'labels' => $labels,
|
||||
'data' => $data,
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,12 @@
|
|||
use Illuminate\Support\Facades\DB;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class PaymentController extends Controller
|
||||
{
|
||||
public function indexManualPayment(cekDenda $cekDenda, GenerateMonthlyBill $generateMonthlyBill)
|
||||
{
|
||||
$penalty = $cekDenda->applyPenalty();
|
||||
$bill = $generateMonthlyBill->generateAutoBill();
|
||||
|
||||
$paymentTypes = PaymentType::get(['id', 'payment_type', 'nominal']);
|
||||
|
||||
|
@ -29,12 +28,11 @@ public function indexManualPayment(cekDenda $cekDenda, GenerateMonthlyBill $gene
|
|||
|
||||
$santri = User::with([
|
||||
'payments.detailPayments.paymentType',
|
||||
])->paginate(10);
|
||||
])->where('level', 2)->paginate(10);
|
||||
|
||||
return Inertia::render('list-admin/payment/ManualPayment', [
|
||||
'santri' => $santri->items(),
|
||||
'penalty' => $penalty,
|
||||
'bill' => $bill,
|
||||
'santri' => $santri,
|
||||
'penalty' => 0,
|
||||
'fields' => [
|
||||
'nis' => ['type' => 'text', 'readonly' => true],
|
||||
'nama' => ['type' => 'text', 'readonly' => true],
|
||||
|
@ -48,113 +46,85 @@ public function indexManualPayment(cekDenda $cekDenda, GenerateMonthlyBill $gene
|
|||
]);
|
||||
}
|
||||
|
||||
public function manualPayment(Request $request, $paymentId)
|
||||
public function manualPayment(Request $request, $userId)
|
||||
{
|
||||
// $request->validate([
|
||||
// 'items' => 'required|array',
|
||||
// 'items.*.type_id' => 'required|exists:payment_types,id',
|
||||
// 'items.*.range' => 'required|integer|min:1'
|
||||
// ]);
|
||||
$validator = Validator::make($request->all(), [
|
||||
'items' => 'required|array',
|
||||
'items.*.type_id' => 'required|exists:payment_types,id',
|
||||
'items.*.range' => 'required|integer|min:1',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return redirect()->back()->withErrors($validator)->withInput();
|
||||
}
|
||||
|
||||
$items = $request->input('items');
|
||||
$userId = $request->id;
|
||||
$items = json_decode($request->input('items'), true);
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
|
||||
$paymentTypes = PaymentType::pluck('nominal', 'id');
|
||||
$user = User::findOrFail($userId);
|
||||
|
||||
$existingPayment = Payment::where('user_id', $userId)
|
||||
->where('payment_status', 'pending')
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if ($existingPayment) {
|
||||
$totalAmount = 0;
|
||||
|
||||
foreach ($items as $item) {
|
||||
$typeId = $item['type_id'];
|
||||
$range = $item['range'];
|
||||
$nominal = $paymentTypes[$typeId] ?? 0;
|
||||
|
||||
$unpaidDetails = DetailPayment::where('payment_id', $existingPayment->id)
|
||||
->where('status', 'unpaid')
|
||||
->where('type_id', $typeId)
|
||||
->orderBy('payment_year')
|
||||
->orderBy('payment_month')
|
||||
->get();
|
||||
|
||||
$toPay = $unpaidDetails->take($range);
|
||||
$toPay->each(function ($detail) use (&$totalAmount, $nominal) {
|
||||
$detail->update([
|
||||
'status' => 'paid',
|
||||
'amount' => $nominal,
|
||||
'penalty' => $detail->penalty ?? 0,
|
||||
]);
|
||||
$totalAmount += $nominal + $detail->penalty;
|
||||
});
|
||||
|
||||
$sisa = $range - $toPay->count();
|
||||
if ($sisa > 0) {
|
||||
$lastDetail = DetailPayment::where('payment_id', $existingPayment->id)
|
||||
->orderBy('payment_year', 'desc')
|
||||
->orderBy('payment_month', 'desc')
|
||||
->first();
|
||||
|
||||
$bulan = $lastDetail->payment_month ?? now()->month;
|
||||
$tahun = $lastDetail->payment_year ?? now()->year;
|
||||
|
||||
for ($i = 0; $i < $sisa; $i++) {
|
||||
$bulan++;
|
||||
if ($bulan > 12) {
|
||||
$bulan = 1;
|
||||
$tahun++;
|
||||
}
|
||||
|
||||
DetailPayment::create([
|
||||
'payment_id' => $existingPayment->id,
|
||||
'payment_month' => $bulan,
|
||||
'payment_year' => $tahun,
|
||||
'amount' => $nominal,
|
||||
'penalty' => 0,
|
||||
'status' => 'paid',
|
||||
'type_id' => $typeId,
|
||||
]);
|
||||
|
||||
$totalAmount += $nominal;
|
||||
}
|
||||
}
|
||||
}
|
||||
$totalAmountExisting = DetailPayment::where('payment_id', $existingPayment->id)->sum('amount');
|
||||
$hasUnpaid = DetailPayment::where('payment_id', $existingPayment->id)
|
||||
->where('status', 'unpaid')
|
||||
->exists();
|
||||
|
||||
$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'
|
||||
'amount_payment' => $totalAmountExisting,
|
||||
'payment_status' => $hasUnpaid ? 'pending' : 'success',
|
||||
]);
|
||||
|
||||
DB::commit();
|
||||
return response()->json(['message' => 'Pembayaran berhasil diupdate']);
|
||||
|
||||
return redirect()->back()->with('success', 'Pembayaran yang pending sudah diupdate.');
|
||||
}
|
||||
|
||||
$newPayment = Payment::create([
|
||||
'payment_status' => 'success',
|
||||
'amount_payment' => 0,
|
||||
'transaction_type' => 'payment',
|
||||
'user_id' => $userId,
|
||||
'order_id' => 'TRX' . uniqid(),
|
||||
]);
|
||||
|
||||
$bulan = now()->month;
|
||||
$tahun = now()->year;
|
||||
$totalAmount = 0;
|
||||
|
||||
foreach ($items as $item) {
|
||||
foreach ($request->items as $item) {
|
||||
$typeId = $item['type_id'];
|
||||
$range = $item['range'];
|
||||
$range = (int) $item['range'];
|
||||
$nominal = $paymentTypes[$typeId] ?? 0;
|
||||
|
||||
$lastDetail = DetailPayment::whereHas('payments', function ($q) use ($userId) {
|
||||
$q->where('user_id', $userId);
|
||||
})
|
||||
->where('type_id', $typeId)
|
||||
->orderBy('payment_year', 'desc')
|
||||
->orderBy('payment_month', 'desc')
|
||||
->first();
|
||||
|
||||
if ($lastDetail) {
|
||||
$bulan = $lastDetail->payment_month;
|
||||
$tahun = $lastDetail->payment_year;
|
||||
} else {
|
||||
$bulan = now()->month;
|
||||
$tahun = now()->year;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $range; $i++) {
|
||||
if ($i > 0 || $lastDetail) {
|
||||
$bulan++;
|
||||
if ($bulan > 12) {
|
||||
$bulan = 1;
|
||||
$tahun++;
|
||||
}
|
||||
}
|
||||
|
||||
DetailPayment::create([
|
||||
'payment_id' => $newPayment->id,
|
||||
'payment_month' => $bulan,
|
||||
|
@ -166,24 +136,29 @@ public function manualPayment(Request $request, $paymentId)
|
|||
]);
|
||||
|
||||
$totalAmount += $nominal;
|
||||
|
||||
$bulan++;
|
||||
if ($bulan > 12) {
|
||||
$bulan = 1;
|
||||
$tahun++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$newPayment->update(['amount_payment' => $totalAmount]);
|
||||
DB::commit();
|
||||
// dd('berhasil lur');
|
||||
|
||||
return redirect()->back()->with(['success' => 'Pembayaran baru berhasil dibuat']);
|
||||
} catch (Exception $e) {
|
||||
DB::commit();
|
||||
|
||||
return redirect()->back()->with('success', 'Pembayaran baru berhasil dibuat');
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
// dd('gagal' . $e->getMessage());
|
||||
return redirect()->back()->with(['error' => 'gagal' . $e->getMessage()]);
|
||||
return redirect()->back()->with('error', 'Gagal membuat pembayaran: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function transaction()
|
||||
{
|
||||
$transaction = User::with('payments', 'payments.detailPayments', 'wallet.walletTransactions', 'payments.detailPayments.paymentType')
|
||||
->where('level', 10)
|
||||
->paginate(2);
|
||||
|
||||
// dd($transaction);
|
||||
|
||||
return Inertia::render('list-admin/payment/Transaction', compact('transaction'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Wallet;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
@ -13,7 +14,7 @@ class SantriController extends Controller
|
|||
{
|
||||
public function index()
|
||||
{
|
||||
$santri = User::all();
|
||||
$santri = User::where('level', 2)->paginate(10);
|
||||
return Inertia::render('list-admin/santri/IndexSantri', [
|
||||
'santri' => $santri,
|
||||
'fields' => [
|
||||
|
@ -88,6 +89,8 @@ public function store(Request $request)
|
|||
'foto' => $fotoPath
|
||||
]);
|
||||
|
||||
$santri->wallet()->create(['saldo' => 0]);
|
||||
|
||||
// dd($santri);
|
||||
return redirect()->back()->with('success', 'Data berhasil ditambahkan');
|
||||
} catch (\Throwable $th) {
|
||||
|
|
|
@ -10,8 +10,9 @@ class WalletController extends Controller
|
|||
{
|
||||
public function walletUser()
|
||||
{
|
||||
$wallet = User::with('wallet')->get();
|
||||
// dd($wallet);
|
||||
$wallet = User::with('wallet')
|
||||
->where('level', 2)
|
||||
->paginate(10);
|
||||
|
||||
return Inertia::render('list-admin/payment/WalletUser', compact('wallet'));
|
||||
}
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\WalletTransaction;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class WalletTransactionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(WalletTransaction $walletTransaction)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(WalletTransaction $walletTransaction)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, WalletTransaction $walletTransaction)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(WalletTransaction $walletTransaction)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
|
@ -15,10 +15,12 @@ public function up(): void
|
|||
$table->id();
|
||||
$table->string('order_id')->unique()->nullable();
|
||||
$table->enum('payment_status', ['pending', 'failed', 'success']);
|
||||
$table->enum('transaction_type', ['topup', 'payment']);
|
||||
$table->decimal('amount_payment')->nullable();
|
||||
$table->String('bank')->nullable();
|
||||
$table->string('no_va')->nullable();
|
||||
$table->dateTime('expired_at')->nullable();
|
||||
$table->string('snap_token')->nullable();
|
||||
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
|
||||
$table->foreignId('wallet_id')->nullable()->constrained('wallets')->onDelete('cascade');
|
||||
$table->timestamps();
|
||||
|
|
After Width: | Height: | Size: 181 KiB |
After Width: | Height: | Size: 509 KiB |
After Width: | Height: | Size: 160 KiB |
After Width: | Height: | Size: 721 KiB |
After Width: | Height: | Size: 906 KiB |
After Width: | Height: | Size: 906 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 226 KiB |
After Width: | Height: | Size: 226 KiB |
After Width: | Height: | Size: 64 KiB |
|
@ -30,9 +30,9 @@ function Header() {
|
|||
setTheme(prevTheme => prevTheme === "dark" ? "light" : "dark");
|
||||
}
|
||||
|
||||
const openNotification = () => {
|
||||
dispatch(openRightDrawer({ header: "Notifications", bodyType: RIGHT_DRAWER_TYPES.NOTIFICATION }))
|
||||
}
|
||||
// const openNotification = () => {
|
||||
// dispatch(openRightDrawer({ header: "Notifications", bodyType: RIGHT_DRAWER_TYPES.NOTIFICATION }))
|
||||
// }
|
||||
|
||||
const { post } = useForm()
|
||||
const logoutUser = (e) => {
|
||||
|
@ -54,12 +54,12 @@ function Header() {
|
|||
{theme === "dark" ? <SunIcon className="w-6 h-6" /> : <MoonIcon className="w-6 h-6" />}
|
||||
</button>
|
||||
|
||||
<button className="btn btn-ghost ml-4 btn-circle" onClick={openNotification}>
|
||||
{/* <button className="btn btn-ghost ml-4 btn-circle" onClick={openNotification}>
|
||||
<div className="indicator">
|
||||
<BellIcon className="h-6 w-6" />
|
||||
{noOfNotifications > 0 && <span className="indicator-item badge badge-secondary badge-sm">{noOfNotifications}</span>}
|
||||
</div>
|
||||
</button>
|
||||
</button> */}
|
||||
|
||||
<div className="dropdown dropdown-end ml-4">
|
||||
<label tabIndex={0} className="btn btn-ghost btn-circle avatar">
|
||||
|
@ -71,8 +71,6 @@ function Header() {
|
|||
<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>
|
||||
<div className="divider mt-0 mb-0"></div>
|
||||
<li><a href="#" onClick={logoutUser}>Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
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({});
|
||||
|
@ -86,11 +92,17 @@ const ModalInput = ({ fields, tableName, options, initialData, onClose, showPaym
|
|||
...detail,
|
||||
type_id: parseInt(type_id)
|
||||
}));
|
||||
console.log('Payment Details:', detailsArray);
|
||||
formDataObj.append('items', JSON.stringify(detailsArray));
|
||||
|
||||
// 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}`;
|
||||
|
@ -107,6 +119,14 @@ const ModalInput = ({ fields, tableName, options, initialData, onClose, showPaym
|
|||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (formData.foto instanceof File) {
|
||||
const url = URL.createObjectURL(formData.foto);
|
||||
return () => URL.revokeObjectURL(url);
|
||||
}
|
||||
}, [formData.foto]);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -141,12 +161,26 @@ const ModalInput = ({ fields, tableName, options, initialData, onClose, showPaym
|
|||
))}
|
||||
</select>
|
||||
) : type === "file" ? (
|
||||
<input
|
||||
type="file"
|
||||
name={field}
|
||||
onChange={handleChange}
|
||||
className="file-input file-input-info focus:outline-none focus:ring-1 ring-info w-full"
|
||||
/>
|
||||
<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"
|
||||
|
@ -191,10 +225,9 @@ const ModalInput = ({ fields, tableName, options, initialData, onClose, showPaym
|
|||
<option disabled value="">
|
||||
Pilih Payment Type
|
||||
</option>
|
||||
// Di bagian dropdown payment type:
|
||||
{options.payment_type &&
|
||||
Object.entries(options.payment_type).map(([id, name]) => (
|
||||
<option key={id} value={id}> {/* Gunakan ID sebagai value */}
|
||||
<option key={id} value={id}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Head, Link, useForm } from '@inertiajs/react';
|
||||
import { usePage } from '@inertiajs/react';
|
||||
import Swal from "sweetalert2"
|
||||
// import { InertiaLink, usePage } from '@inertiajs/react';
|
||||
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
function Login({ status }) {
|
||||
const { data, setData, post, processing, errors, reset } = useForm({
|
||||
|
@ -11,6 +11,7 @@ function Login({ status }) {
|
|||
remember: false,
|
||||
});
|
||||
const { flash } = usePage().props;
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (flash.success) {
|
||||
|
@ -59,30 +60,41 @@ function Login({ status }) {
|
|||
name="nis"
|
||||
value={data.nis}
|
||||
className="w-full p-3 border border-gray-700 rounded-lg text-gray-700"
|
||||
placeholder="name@company.com"
|
||||
placeholder="Masukkan Nis Anda"
|
||||
autoComplete="username"
|
||||
onChange={(e) => setData('nis', e.target.value)}
|
||||
/>
|
||||
{errors.nis && <p className="mt-1 text-sm text-red-500">{errors.nis}</p>}
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<div className="mb-6 relative">
|
||||
<label htmlFor="password" className="block text-xl mb-2 text-white">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
name="password"
|
||||
value={data.password}
|
||||
className="w-full p-3 border border-gray-700 rounded-lg"
|
||||
className="w-full p-3 border border-gray-700 rounded-lg pr-12"
|
||||
placeholder="••••••••"
|
||||
autoComplete="current-password"
|
||||
onChange={(e) => setData('password', e.target.value)}
|
||||
/>
|
||||
<div
|
||||
className="absolute top-[50px] right-4 cursor-pointer text-gray-400 hover:text-black"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeSlashIcon className="h-5 w-5" />
|
||||
) : (
|
||||
<EyeIcon className="h-5 w-5" />
|
||||
)}
|
||||
</div>
|
||||
{errors.password && <p className="mt-1 text-sm text-red-500">{errors.password}</p>}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
|
|
|
@ -1,48 +1,45 @@
|
|||
import React from 'react';
|
||||
import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, ArcElement } from 'chart.js';
|
||||
import { Bar, Doughnut } from 'react-chartjs-2';
|
||||
import { Head } from '@inertiajs/react';
|
||||
import { useEffect } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
BarElement,
|
||||
Title,
|
||||
Tooltip,
|
||||
Legend,
|
||||
ArcElement,
|
||||
} from 'chart.js';
|
||||
import { Bar } from 'react-chartjs-2';
|
||||
import { Head, usePage } from '@inertiajs/react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setPageTitle } from '@/Components/features/common/headerSlice';
|
||||
import Squares2X2Icon from '@heroicons/react/24/outline/Squares2X2Icon'
|
||||
import { Squares2X2Icon } from '@heroicons/react/24/outline';
|
||||
|
||||
ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, ArcElement);
|
||||
|
||||
const Dashboard = () => {
|
||||
const barChartData = {
|
||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'CHN',
|
||||
data: [12, 19, 8, 15, 12, 8, 16, 20, 15, 14],
|
||||
backgroundColor: 'rgb(196, 151, 239)',
|
||||
},
|
||||
{
|
||||
label: 'USA',
|
||||
data: [19, 12, 15, 8, 9, 16, 14, 12, 10, 15],
|
||||
backgroundColor: 'rgb(86, 207, 225)',
|
||||
},
|
||||
{
|
||||
label: 'UK',
|
||||
data: [15, 15, 10, 12, 20, 14, 8, 16, 10, 18],
|
||||
backgroundColor: 'rgb(255, 170, 145)',
|
||||
},
|
||||
],
|
||||
};
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const doughnutChartData = {
|
||||
labels: ['Social Media', 'Direct', 'Email', 'Organic'],
|
||||
// Ambil data dari Inertia props
|
||||
const { props } = usePage();
|
||||
const {
|
||||
monthlyIncome = 0,
|
||||
studentCount = 0,
|
||||
totalBalance = 0,
|
||||
paymentTrend = { labels: [], data: [] },
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setPageTitle('Dashboard'));
|
||||
}, [dispatch]);
|
||||
|
||||
const barChartData = {
|
||||
labels: paymentTrend.labels,
|
||||
datasets: [
|
||||
{
|
||||
data: [35, 30, 15, 20],
|
||||
backgroundColor: [
|
||||
'rgb(255, 99, 132)',
|
||||
'rgb(54, 162, 235)',
|
||||
'rgb(153, 102, 255)',
|
||||
'rgb(75, 192, 192)',
|
||||
],
|
||||
borderWidth: 0,
|
||||
label: 'Pemasukan Bulanan',
|
||||
data: paymentTrend.data,
|
||||
backgroundColor: 'rgb(86, 207, 225)',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -50,45 +47,34 @@ const Dashboard = () => {
|
|||
const barChartOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
legend: { display: false },
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Tren Pemasukan Bulanan',
|
||||
font: { size: 16, weight: 'bold' },
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
return `Rp ${context.parsed.y.toLocaleString()}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
x: { grid: { display: false } },
|
||||
y: {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
grid: { display: false },
|
||||
ticks: {
|
||||
display: false,
|
||||
callback: function (value) {
|
||||
return `Rp ${value.toLocaleString()}`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
maintainAspectRatio: false,
|
||||
};
|
||||
|
||||
const doughnutChartOptions = {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
cutout: '70%',
|
||||
maintainAspectRatio: false,
|
||||
};
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setPageTitle("Dashboard"));
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<div className="card bg-base-100 shadow-xl">
|
||||
<Head title="Dashboard" />
|
||||
|
@ -100,117 +86,45 @@ const Dashboard = () => {
|
|||
<h1 className="text-2xl font-bold">Dashboard</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>
|
||||
|
||||
{/* Kartu info utama */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
||||
<div className="rounded-lg overflow-hidden bg-gradient-to-r from-orange-300 to-pink-400 text-white p-6">
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
<p className="text-xl font-medium mb-4">Weekly Sales</p>
|
||||
<p className="text-4xl font-bold mb-6">$ 15,0000</p>
|
||||
<p>Increased by 60%</p>
|
||||
</div>
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-white opacity-80" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xl font-medium mb-4">Pemasukan Bulan Ini</p>
|
||||
<p className="text-4xl font-bold mb-6">
|
||||
Rp {(Number(monthlyIncome) || 0).toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg overflow-hidden bg-gradient-to-r from-blue-300 to-blue-500 text-white p-6">
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
<p className="text-xl font-medium mb-4">Weekly Orders</p>
|
||||
<p className="text-4xl font-bold mb-6">45,6334</p>
|
||||
<p>Decreased by 10%</p>
|
||||
</div>
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-white opacity-80" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xl font-medium mb-4">Santri Aktif</p>
|
||||
<p className="text-4xl font-bold mb-6">{studentCount || 0}</p>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg overflow-hidden bg-gradient-to-r from-green-300 to-teal-500 text-white p-6">
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
<p className="text-xl font-medium mb-4">Visitors Online</p>
|
||||
<p className="text-4xl font-bold mb-6">95,5741</p>
|
||||
<p>Increased by 5%</p>
|
||||
</div>
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-white opacity-80" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 12l-1.25-1.25M12 20l1.25-1.25M4 12l1.25 1.25M12 4l-1.25 1.25M20 12h-2M12 20v-2M4 12h2M12 4v2" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xl font-medium mb-4">Total Saldo Wallet</p>
|
||||
<p className="text-4xl font-bold mb-6">
|
||||
Rp {(Number(totalBalance) || 0).toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div className=" rounded-lg p-6 shadow">
|
||||
{/* Grafik Tren Pembayaran */}
|
||||
<div className="w-full bg-base-100">
|
||||
<div className="rounded-lg p-6 shadow">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h2 className="text-xl font-bold">Visit And Sales Statistics</h2>
|
||||
<div className="flex space-x-4">
|
||||
<div className="flex items-center">
|
||||
<span className="h-3 w-3 rounded-full bg-purple-400 mr-1"></span>
|
||||
<span className="text-gray-500 text-sm">CHN</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<span className="h-3 w-3 rounded-full bg-cyan-400 mr-1"></span>
|
||||
<span className="text-gray-500 text-sm">USA</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<span className="h-3 w-3 rounded-full bg-orange-300 mr-1"></span>
|
||||
<span className="text-gray-500 text-sm">UK</span>
|
||||
</div>
|
||||
</div>
|
||||
<h2 className="text-xl font-bold">Tren Pembayaran 12 Bulan Terakhir</h2>
|
||||
</div>
|
||||
<div className="h-64">
|
||||
<Bar data={barChartData} options={barChartOptions} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg p-6 shadow">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-bold">Traffic Sources</h2>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<div className="h-64 w-64">
|
||||
<Doughnut data={doughnutChartData} options={doughnutChartOptions} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 mt-6">
|
||||
<div className="flex items-center justify-center">
|
||||
<span className="h-3 w-3 rounded-full bg-pink-400 mr-2"></span>
|
||||
<span className="text-gray-700 text-sm">Social Media</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-center">
|
||||
<span className="h-3 w-3 rounded-full bg-blue-400 mr-2"></span>
|
||||
<span className="text-gray-700 text-sm">Direct</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-center">
|
||||
<span className="h-3 w-3 rounded-full bg-purple-400 mr-2"></span>
|
||||
<span className="text-gray-700 text-sm">Email</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-center">
|
||||
<span className="h-3 w-3 rounded-full bg-teal-400 mr-2"></span>
|
||||
<span className="text-gray-700 text-sm">Organic</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
export default Dashboard;
|
||||
|
|
|
@ -17,35 +17,20 @@ export default function ManualPayment({ santri, fields, options }) {
|
|||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (flash.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success',
|
||||
text: flash.success
|
||||
});
|
||||
} else if (flash.error) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success',
|
||||
text: flash.success
|
||||
});
|
||||
}
|
||||
}, [flash]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setPageTitle("Data Payment"));
|
||||
}, [dispatch]);
|
||||
|
||||
// console.log(santri.id)
|
||||
useEffect(() => {
|
||||
if (santri) {
|
||||
setFilteredSantri(
|
||||
santri.filter(item =>
|
||||
if (santri?.data) {
|
||||
if (santri?.data) {
|
||||
const filtered = santri.data.filter(item =>
|
||||
item.nama.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
item.nis.toString().includes(searchTerm)
|
||||
)
|
||||
);
|
||||
);
|
||||
setFilteredSantri(filtered);
|
||||
}
|
||||
}
|
||||
}, [searchTerm, santri]);
|
||||
|
||||
|
@ -53,6 +38,12 @@ export default function ManualPayment({ santri, fields, options }) {
|
|||
setSearchTerm(e.target.value);
|
||||
};
|
||||
|
||||
const monthNames = [
|
||||
"Januari", "Februari", "Maret", "April", "Mei", "Juni",
|
||||
"Juli", "Agustus", "September", "Oktober", "November", "Desember"
|
||||
];
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -163,22 +154,45 @@ export default function ManualPayment({ santri, fields, options }) {
|
|||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="flex justify-center mt-4">
|
||||
{santri?.links?.map((link, index) => (
|
||||
</div>
|
||||
<div className="flex justify-center mt-4">
|
||||
{santri.current_page > 1 && (
|
||||
<button className="btn btn-sm mx-1" onClick={() => (window.location.href = `?page=1`)}>
|
||||
«
|
||||
</button>
|
||||
)}
|
||||
{santri.current_page > 3 && (
|
||||
<span className="btn btn-sm mx-1 pointer-events-none">...</span>
|
||||
)}
|
||||
|
||||
{Array.from({ length: santri.last_page }, (_, i) => i + 1)
|
||||
.filter((page) =>
|
||||
page === 1 ||
|
||||
page === santri.last_page ||
|
||||
Math.abs(page - santri.current_page) <= 2
|
||||
)
|
||||
.map((page) => (
|
||||
<button
|
||||
key={index}
|
||||
className={`px-4 py-2 mx-1 ${link.active ? "bg-blue-500 text-white" : "bg-gray-200"
|
||||
}`}
|
||||
key={page}
|
||||
className={`btn btn-sm mx-1 ${santri.current_page === page ? 'btn-active btn-primary text-white' : ''}`}
|
||||
onClick={() => {
|
||||
if (link.url) window.location.href = link.url;
|
||||
window.location.href = `?page=${page}`;
|
||||
}}
|
||||
disabled={!link.url}
|
||||
>
|
||||
{link.label.replace('«', '«').replace('»', '»')}
|
||||
{page}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{santri.current_page < santri.last_page - 2 && (
|
||||
<span className="btn btn-sm mx-1 pointer-events-none">...</span>
|
||||
)}
|
||||
{santri.current_page < santri.last_page && (
|
||||
<button className="btn btn-sm mx-1" onClick={() => (window.location.href = `?page=${santri.last_page}`)}>
|
||||
»
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -205,7 +219,7 @@ export default function ManualPayment({ santri, fields, options }) {
|
|||
<tr key={`${idx}-${detail.id}`}>
|
||||
<td>{detail.payment_type?.payment_type}</td>
|
||||
<td className="text-center">{detail.payment_year}</td>
|
||||
<td className="text-center">{detail.payment_month}</td>
|
||||
<td className="text-center">{monthNames[detail.payment_month - 1] || detail.payment_month}</td>
|
||||
<td className="text-center">{detail.penalty}</td>
|
||||
<td className="text-center">
|
||||
<span className={`p-2 rounded-md ${detail.status === 'paid' ? 'bg-green-500 text-white' : 'bg-red-600 text-white'}`}>
|
||||
|
@ -215,6 +229,7 @@ export default function ManualPayment({ santri, fields, options }) {
|
|||
</tr>
|
||||
))
|
||||
)}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -4,34 +4,15 @@ import ModalInput from '@/Components/ModalInput';
|
|||
import DeleteButton from '@/Components/DeleteButton';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setPageTitle } from '@/Components/features/common/headerSlice';
|
||||
import { usePage } from '@inertiajs/react';
|
||||
import Swal from "sweetalert2"
|
||||
|
||||
export default function PaymentType({ paymentType, fields }) {
|
||||
const [selectedPaymentType, setSelectedPaymentType] = useState(null);
|
||||
const [isDeleteOpen, setDeleteOpen] = useState(false);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [filteredPaymentTypes, setFilteredPaymentTypes] = useState([]);
|
||||
const { flash } = usePage().props;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (flash.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success',
|
||||
text: flash.success
|
||||
});
|
||||
} else if (flash.error) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success',
|
||||
text: flash.success
|
||||
});
|
||||
}
|
||||
}, [flash]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setPageTitle("Data Tipe Pembayaran"));
|
||||
}, [dispatch]);
|
||||
|
|
|
@ -0,0 +1,239 @@
|
|||
// Import wajib
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Head } from '@inertiajs/react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setPageTitle } from '@/Components/features/common/headerSlice';
|
||||
import { CurrencyDollarIcon } from '@heroicons/react/24/outline';
|
||||
import { Inertia } from '@inertiajs/inertia';
|
||||
|
||||
export default function Transaction({ transaction }) {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [filteredSantri, setFilteredSantri] = useState([]);
|
||||
const [selectedSantri, setSelectedSantri] = useState(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [selectedMonth, setSelectedMonth] = useState(new Date().getMonth() + 1);
|
||||
const [selectedYear, setSelectedYear] = useState(new Date().getFullYear());
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const bulanIndo = [
|
||||
'Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni',
|
||||
'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember'
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setPageTitle("Data Transaksi Santri"));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (transaction?.data) {
|
||||
const filtered = transaction.data.filter(item => {
|
||||
const matchSearch = item.nama.toLowerCase().includes(searchTerm.toLowerCase()) || item.nis?.toString().includes(searchTerm);
|
||||
const hasPaymentThisMonthYear = item.payments?.some(payment =>
|
||||
payment.detail_payments?.some(d =>
|
||||
d.payment_month === selectedMonth &&
|
||||
(selectedYear === 'all' || d.payment_year === selectedYear)
|
||||
)
|
||||
);
|
||||
return matchSearch && hasPaymentThisMonthYear;
|
||||
});
|
||||
setFilteredSantri(filtered);
|
||||
}
|
||||
}, [searchTerm, selectedMonth, selectedYear, transaction]);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
setSelectedSantri(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Head title="Transaksi 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 Transaksi User</h1>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center mb-4 gap-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Cari Santri..."
|
||||
className="input input-bordered"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<select
|
||||
className="select select-bordered"
|
||||
value={selectedMonth}
|
||||
onChange={(e) => setSelectedMonth(Number(e.target.value))}
|
||||
>
|
||||
{bulanIndo.map((b, i) => <option key={i} value={i + 1}>{b}</option>)}
|
||||
</select>
|
||||
<select
|
||||
className="select select-bordered"
|
||||
value={selectedYear}
|
||||
onChange={(e) => setSelectedYear(e.target.value === 'all' ? 'all' : Number(e.target.value))}
|
||||
>
|
||||
<option value="all">Semua Tahun</option>
|
||||
{Array.from({ length: 5 }, (_, i) => {
|
||||
const year = new Date().getFullYear() - 2 + i;
|
||||
return <option key={year} value={year}>{year}</option>
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* TABEL SANTRI */}
|
||||
<div className="overflow-x-auto">
|
||||
<table className="table table-zebra w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama</th>
|
||||
<th>Detail</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>
|
||||
<button
|
||||
className='btn btn-success text-white'
|
||||
onClick={() => {
|
||||
setSelectedSantri(item);
|
||||
setIsModalOpen(true);
|
||||
}}
|
||||
>
|
||||
Detail
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="2" className="text-center">Tidak ada data santri.</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* PAGINATION */}
|
||||
<div className="flex justify-center mt-4">
|
||||
{transaction.current_page > 1 && (
|
||||
<button
|
||||
className="btn btn-sm mx-1"
|
||||
onClick={() =>
|
||||
Inertia.visit(`${window.location.pathname}?page=${transaction.current_page - 1}`)
|
||||
}
|
||||
>«</button>
|
||||
)}
|
||||
{Array.from({ length: transaction.last_page }, (_, i) => i + 1)
|
||||
.filter(page => page === 1 || page === transaction.last_page || Math.abs(page - transaction.current_page) <= 2)
|
||||
.map((page, i, arr) => {
|
||||
const prev = arr[i - 1];
|
||||
const showDots = prev && page - prev > 1;
|
||||
return (
|
||||
<React.Fragment key={page}>
|
||||
{showDots && <span className="btn btn-sm mx-1 pointer-events-none">...</span>}
|
||||
<button
|
||||
className={`btn btn-sm mx-1 ${transaction.current_page === page ? 'btn-active btn-primary text-white' : ''}`}
|
||||
onClick={() =>
|
||||
Inertia.visit(`${window.location.pathname}?page=${page}`)
|
||||
}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
{transaction.current_page < transaction.last_page && (
|
||||
<button
|
||||
className="btn btn-sm mx-1"
|
||||
onClick={() =>
|
||||
Inertia.visit(`${window.location.pathname}?page=${transaction.current_page + 1}`)
|
||||
}
|
||||
>»</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* MODAL DETAIL TRANSAKSI */}
|
||||
{isModalOpen && selectedSantri && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" onClick={closeModal}>
|
||||
<div className="bg-base-100 rounded-2xl shadow-xl p-6 w-11/12 max-w-3xl relative" onClick={e => e.stopPropagation()}>
|
||||
<button onClick={closeModal} className="absolute top-3 right-3 text-red-500 hover:text-red-700">❌</button>
|
||||
<h2 className="text-2xl font-bold mb-4 text-center text-gray-800">
|
||||
Detail Transaksi <span className="text-blue-600">{selectedSantri.nama}</span>
|
||||
</h2>
|
||||
|
||||
<div className="space-y-4 max-h-[70vh] overflow-y-auto">
|
||||
{selectedSantri.payments
|
||||
?.map(p => ({
|
||||
...p,
|
||||
details: p.detail_payments?.filter(d => d.payment_month === selectedMonth && (selectedYear === 'all' || d.payment_year === selectedYear))
|
||||
}))
|
||||
.filter(p => p.details?.length)
|
||||
.map((payment, idx) => {
|
||||
const total = payment.details.reduce((sum, d) => sum + Number(d.amount) + Number(d.penalty || 0), 0);
|
||||
return (
|
||||
<div key={idx} className="bg-base-200 p-4 rounded-xl shadow-md border border-gray-200">
|
||||
<div className="flex flex-wrap justify-between items-center text-sm mb-2">
|
||||
<div><strong>Order ID:</strong> {payment.order_id || '-'}</div>
|
||||
<div className={payment.payment_status === 'success' ? 'bg-green-600 text-white px-2 py-1 rounded' : 'bg-orange-600 text-white px-2 py-1 rounded'}>
|
||||
{payment.payment_status}
|
||||
</div>
|
||||
</div>
|
||||
<table className="table w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100 text-gray-700">
|
||||
<th>Status</th>
|
||||
<th>Jumlah</th>
|
||||
<th>Jenis</th>
|
||||
<th>Tipe</th>
|
||||
<th>Denda</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{payment.details.map((detail, i) => (
|
||||
<tr key={i}>
|
||||
<td>{detail.status}</td>
|
||||
<td>Rp {Number(detail.amount).toLocaleString()}</td>
|
||||
<td>{payment.transaction_type === 'payment' ? 'Pembayaran' : 'Topup'}</td>
|
||||
<td>{detail.payment_type?.payment_type || '-'}</td>
|
||||
<td>Rp {Number(detail.penalty || 0).toLocaleString()}</td>
|
||||
<td>Rp {(Number(detail.amount) + Number(detail.penalty || 0)).toLocaleString()}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="text-right mt-2 font-bold text-black">
|
||||
Total: Rp {total.toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -18,9 +18,9 @@ export default function ManualPayment({ wallet }) {
|
|||
|
||||
// console.log(santri.id)
|
||||
useEffect(() => {
|
||||
if (wallet) {
|
||||
if (wallet?.data) {
|
||||
setFilteredSantri(
|
||||
wallet.filter(item =>
|
||||
wallet.data.filter(item =>
|
||||
item.nama.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
item.nis.toString().includes(searchTerm)
|
||||
)
|
||||
|
@ -100,7 +100,7 @@ export default function ManualPayment({ wallet }) {
|
|||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{item.wallet ? item.wallet.saldo : 'Tidak ada saldo'}
|
||||
Rp. {item.wallet ? item.wallet.saldo : 'Tidak ada saldo'}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
|
@ -112,20 +112,53 @@ export default function ManualPayment({ wallet }) {
|
|||
</tbody>
|
||||
</table>
|
||||
<div className="flex justify-center mt-4">
|
||||
{wallet?.links?.map((link, index) => (
|
||||
{wallet.current_page > 1 && (
|
||||
<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}
|
||||
className="btn btn-sm mx-1"
|
||||
onClick={() => (window.location.href = `?page=1`)}
|
||||
>
|
||||
{link.label.replace('«', '«').replace('»', '»')}
|
||||
«
|
||||
</button>
|
||||
))}
|
||||
)}
|
||||
|
||||
{wallet.current_page > 3 && (
|
||||
<span className="btn btn-sm mx-1 pointer-events-none">...</span>
|
||||
)}
|
||||
|
||||
{Array.from({ length: wallet.last_page }, (_, i) => i + 1)
|
||||
.filter(
|
||||
(page) =>
|
||||
page === 1 ||
|
||||
page === wallet.last_page ||
|
||||
Math.abs(page - wallet.current_page) <= 2
|
||||
)
|
||||
.map((page) => (
|
||||
<button
|
||||
key={page}
|
||||
className={`btn btn-sm mx-1 ${wallet.current_page === page ? 'btn-active btn-primary text-white' : ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
window.location.href = `?page=${page}`;
|
||||
}}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
))}
|
||||
|
||||
{wallet.current_page < wallet.last_page - 2 && (
|
||||
<span className="btn btn-sm mx-1 pointer-events-none">...</span>
|
||||
)}
|
||||
|
||||
{wallet.current_page < wallet.last_page && (
|
||||
<button
|
||||
className="btn btn-sm mx-1"
|
||||
onClick={() => (window.location.href = `?page=${wallet.last_page}`)}
|
||||
>
|
||||
»
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Head } from '@inertiajs/react';
|
||||
import { Head, usePage } from '@inertiajs/react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { setPageTitle } from '@/Components/features/common/headerSlice';
|
||||
import ModalInput from '@/Components/ModalInput';
|
||||
import DeleteButton from '@/Components/DeleteButton';
|
||||
import { UserGroupIcon } from '@heroicons/react/24/outline'
|
||||
import { usePage } from '@inertiajs/react';
|
||||
import Swal from "sweetalert2"
|
||||
|
||||
import { UserGroupIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
export default function IndexSantri({ santri, fields, options }) {
|
||||
const [selectedSantri, setSelectedSantri] = useState(null);
|
||||
|
@ -18,38 +15,21 @@ export default function IndexSantri({ santri, fields, options }) {
|
|||
const { flash } = usePage().props;
|
||||
|
||||
const openDeleteModal = (item) => {
|
||||
setSelectedSantri(item)
|
||||
setDeleteOpen(true)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (flash.success) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success',
|
||||
text: flash.success
|
||||
});
|
||||
} else if (flash.error) {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Success',
|
||||
text: flash.success
|
||||
});
|
||||
}
|
||||
}, [flash]);
|
||||
setSelectedSantri(item);
|
||||
setDeleteOpen(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setPageTitle("Data Santri"));
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (santri) {
|
||||
setFilteredSantri(
|
||||
santri.filter(item =>
|
||||
item.nama.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
item.nis.toString().includes(searchTerm)
|
||||
)
|
||||
if (santri?.data) {
|
||||
const filtered = santri.data.filter(item =>
|
||||
item.nama.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
||||
item.nis.toString().includes(searchTerm)
|
||||
);
|
||||
setFilteredSantri(filtered);
|
||||
}
|
||||
}, [searchTerm, santri]);
|
||||
|
||||
|
@ -69,13 +49,9 @@ export default function IndexSantri({ santri, fields, options }) {
|
|||
<h1 className="text-2xl font-bold">Data 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">
|
||||
|
@ -110,53 +86,55 @@ export default function IndexSantri({ santri, fields, options }) {
|
|||
</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" />
|
||||
{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' : 'User'}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold">{item.nama}</div>
|
||||
<div className="text-sm opacity-50">{item.level == 1 ? 'Admin' : 'User'}</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>
|
||||
</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.alamat || "-"}</td>
|
||||
<td>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
className="btn btn-sm btn-primary text-white"
|
||||
onClick={() => {
|
||||
setSelectedSantri(item);
|
||||
document.getElementById('modal_input').checked = true;
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-sm btn-error text-white"
|
||||
onClick={() => openDeleteModal(item)}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)) : (
|
||||
</td>
|
||||
<td>{item.alamat || "-"}</td>
|
||||
<td>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
className="btn btn-sm btn-primary text-white"
|
||||
onClick={() => {
|
||||
setSelectedSantri(item);
|
||||
document.getElementById('modal_input').checked = true;
|
||||
}}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-sm btn-error text-white"
|
||||
onClick={() => openDeleteModal(item)}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="5" className="text-center">Tidak ada data santri.</td>
|
||||
</tr>
|
||||
|
@ -164,6 +142,58 @@ export default function IndexSantri({ santri, fields, options }) {
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center mt-4">
|
||||
{santri.current_page > 1 && (
|
||||
<button
|
||||
className="btn btn-sm mx-1"
|
||||
onClick={() => (window.location.href = `?page=${santri.current_page - 1}`)}
|
||||
>
|
||||
«
|
||||
</button>
|
||||
)}
|
||||
|
||||
{Array.from({ length: santri.last_page }, (_, i) => i + 1)
|
||||
.filter((page) =>
|
||||
page === 1 ||
|
||||
page === santri.last_page ||
|
||||
Math.abs(page - santri.current_page) <= 2
|
||||
)
|
||||
.map((page, index, array) => {
|
||||
const prevPage = array[index - 1];
|
||||
const showEllipsis = prevPage && page - prevPage > 1;
|
||||
|
||||
return (
|
||||
<React.Fragment key={page}>
|
||||
{showEllipsis && (
|
||||
<span className="btn btn-sm mx-1 pointer-events-none">...</span>
|
||||
)}
|
||||
<button
|
||||
className={`btn btn-sm mx-1 ${santri.current_page === page
|
||||
? 'btn-active btn-primary text-white'
|
||||
: ''
|
||||
}`}
|
||||
onClick={() => {
|
||||
window.location.href = `?page=${page}`;
|
||||
}}
|
||||
>
|
||||
{page}
|
||||
</button>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Tombol terakhir */}
|
||||
{santri.current_page < santri.last_page && (
|
||||
<button
|
||||
className="btn btn-sm mx-1"
|
||||
onClick={() => (window.location.href = `?page=${santri.current_page + 1}`)}
|
||||
>
|
||||
»
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -171,7 +201,7 @@ export default function IndexSantri({ santri, fields, options }) {
|
|||
isOpen={isDeleteOpen}
|
||||
onClose={() => setDeleteOpen(false)}
|
||||
item={selectedSantri}
|
||||
tableName="payment_types"
|
||||
tableName="users"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -7,8 +7,9 @@ const IndexSantri = lazy(() => import('@/Pages/list-admin/santri/IndexSantri'))
|
|||
const PaymentType = lazy(() => import('@/Pages/list-admin/payment/PaymentType'))
|
||||
const ManualPayment = lazy(() => import('@/Pages/list-admin/payment/ManualPayment'))
|
||||
const WalletUser = lazy(() => import('@pages/list-admin/wallet/WalletUser'))
|
||||
const Transaction = lazy(() => import('@pages/list-admin/payment/Transaction'))
|
||||
|
||||
console.log(route('dashboard'))
|
||||
// console.log(route('dashboard'))
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
@ -30,6 +31,10 @@ const routes = [
|
|||
{
|
||||
path: route('walletUser'),
|
||||
componenet: WalletUser
|
||||
},
|
||||
{
|
||||
path: route('transaction'),
|
||||
component: Transaction
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -70,6 +70,11 @@ const routes = [
|
|||
path: '/wallet-user',
|
||||
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
||||
name: 'Wallet User'
|
||||
},
|
||||
{
|
||||
path: '/transaksi-user',
|
||||
icon: <ArrowRightIcon className={submenuIconClasses} />,
|
||||
name: 'Transaksi User'
|
||||
}
|
||||
],
|
||||
},
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title inertia>{{ config('app.name', 'GogoSantri') }}</title>
|
||||
<link rel="icon" href="{{ asset('assets/gogoSantri.png') }}">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
use App\Http\Controllers\HomeController;
|
||||
use Inertia\Inertia;
|
||||
|
||||
Route::get('/', function () {
|
||||
return redirect()->route('login');
|
||||
});
|
||||
Route::middleware('guest')->group(function () {
|
||||
Route::get('/', function () {
|
||||
return redirect()->route('login');
|
||||
});
|
||||
|
||||
Route::get('/login', function () {
|
||||
return redirect()->route('login');
|
||||
Route::get('/login', function () {
|
||||
return redirect()->route('login');
|
||||
});
|
||||
});
|
||||
|
||||
Route::middleware('auth')->group(function () {
|
||||
|
@ -46,9 +48,7 @@
|
|||
// wallet
|
||||
Route::get('/wallet-user', [WalletController::class, 'walletUser'])->name('walletUser');
|
||||
|
||||
Route::get('profile-settings', function () {
|
||||
return Inertia::render('ProfileSettings');
|
||||
});
|
||||
Route::get('/transaksi-user', [PaymentController::class, 'transaction'])->name('transaction');
|
||||
});
|
||||
|
||||
require __DIR__ . '/auth.php';
|
||||
|
|