This commit is contained in:
alealien666 2025-06-07 18:52:25 +07:00
parent 91dff8b258
commit 0cc138f0a2
31 changed files with 723 additions and 555 deletions

View File

@ -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)
{
//
}
}

View File

@ -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,
],
]);
}
}

View File

@ -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);
try {
DB::beginTransaction();
try {
$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)
$totalAmountExisting = DetailPayment::where('payment_id', $existingPayment->id)->sum('amount');
$hasUnpaid = 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;
}
}
}
->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'));
}
}

View File

@ -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) {

View File

@ -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'));
}

View File

@ -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)
{
//
}
}

View File

@ -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();

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -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>

View File

@ -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" ? (
<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>
))}

View File

@ -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

View File

@ -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,113 +86,41 @@ 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">
{/* 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>

View File

@ -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>
<div className="flex justify-center mt-4">
{santri?.links?.map((link, index) => (
{santri.current_page > 1 && (
<button className="btn btn-sm mx-1" onClick={() => (window.location.href = `?page=1`)}>
&laquo;
</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('&laquo;', '«').replace('&raquo;', '»')}
{page}
</button>
))}
{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}`)}>
&raquo;
</button>
)}
</div>
</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>

View File

@ -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]);

View File

@ -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}`)
}
>&laquo;</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}`)
}
>&raquo;</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>
);
}

View File

@ -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"
className="btn btn-sm mx-1"
onClick={() => (window.location.href = `?page=1`)}
>
&laquo;
</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={() => {
if (link.url) window.location.href = link.url;
window.location.href = `?page=${page}`;
}}
disabled={!link.url}
>
{link.label.replace('&laquo;', '«').replace('&raquo;', '»')}
{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}`)}
>
&raquo;
</button>
)}
</div>
</div>
</div>
</div>

View File

@ -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 =>
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,7 +86,8 @@ export default function IndexSantri({ santri, fields, options }) {
</tr>
</thead>
<tbody>
{filteredSantri.length > 0 ? filteredSantri.map((item, i) => (
{filteredSantri.length > 0 ? (
filteredSantri.map((item, i) => (
<tr key={i}>
<td>
<div className="flex items-center space-x-3">
@ -121,7 +98,7 @@ export default function IndexSantri({ santri, fields, options }) {
</div>
<div>
<div className="font-bold">{item.nama}</div>
<div className="text-sm opacity-50">{item.level == 1 ? 'Admin' : 'User'}</div>
<div className="text-sm opacity-50">{item.level === 1 ? 'Admin' : 'User'}</div>
</div>
</div>
</td>
@ -156,7 +133,8 @@ export default function IndexSantri({ santri, fields, options }) {
</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}`)}
>
&laquo;
</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}`)}
>
&raquo;
</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>
);

View File

@ -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
}
]

View File

@ -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'
}
],
},

View File

@ -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">

View File

@ -10,6 +10,7 @@
use App\Http\Controllers\HomeController;
use Inertia\Inertia;
Route::middleware('guest')->group(function () {
Route::get('/', function () {
return redirect()->route('login');
});
@ -17,6 +18,7 @@
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';