update tgl 14 juni

This commit is contained in:
whywdd 2025-06-14 17:28:23 +07:00
parent 20eeb0c646
commit 621fec5d21
21 changed files with 900 additions and 70 deletions

View File

@ -13,7 +13,6 @@ public function logout(Request $request)
Session::flush(); Session::flush();
Auth::logout(); Auth::logout();
return redirect()->route('login') return redirect()->route('login');
->with('success', 'Anda berhasil logout.');
} }
} }

View File

@ -4,6 +4,7 @@
use App\Models\DataKaryawanModel; use App\Models\DataKaryawanModel;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Carbon\Carbon;
class DataKaryawanController extends Controller class DataKaryawanController extends Controller
{ {
@ -18,18 +19,29 @@ public function store(Request $request)
// Validasi input // Validasi input
$request->validate([ $request->validate([
'nama' => 'required|string|max:100', 'nama' => 'required|string|max:100',
'usia' => 'required|integer|min:17|max:65', 'tanggal_lahir' => 'required|date',
'jabatan' => 'required|string|max:50', 'jabatan' => 'required|string|max:50',
'gaji' => 'required' 'gaji' => 'required'
]); ]);
// Hitung usia
$tanggalLahir = Carbon::parse($request->tanggal_lahir);
$usia = $tanggalLahir->age;
// Validasi usia
if ($usia < 17 || $usia > 65) {
return redirect()->back()
->with('error', 'Usia harus antara 17-65 tahun')
->withInput();
}
// Bersihkan format angka dari gaji // Bersihkan format angka dari gaji
$gaji = str_replace('.', '', $request->gaji); $gaji = str_replace('.', '', $request->gaji);
// Simpan data // Simpan data
DataKaryawanModel::create([ DataKaryawanModel::create([
'nama' => $request->nama, 'nama' => $request->nama,
'usia' => $request->usia, 'tanggal_lahir' => $tanggalLahir->timestamp, // Simpan sebagai timestamp
'jabatan' => $request->jabatan, 'jabatan' => $request->jabatan,
'gaji' => $gaji 'gaji' => $gaji
]); ]);
@ -39,7 +51,8 @@ public function store(Request $request)
->with('success', 'Data karyawan berhasil ditambahkan!'); ->with('success', 'Data karyawan berhasil ditambahkan!');
} catch (\Exception $e) { } catch (\Exception $e) {
return redirect()->back() return redirect()->back()
->with('error', 'Gagal menambahkan data karyawan: ' . $e->getMessage()); ->with('error', 'Gagal menambahkan data karyawan: ' . $e->getMessage())
->withInput();
} }
} }
} }

View File

@ -4,16 +4,23 @@
use App\Models\GajiModel; // Import model use App\Models\GajiModel; // Import model
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Carbon\Carbon;
class GajiController extends Controller class GajiController extends Controller
{ {
public function index() public function index()
{ {
// Ambil data dari tabel karyawans termasuk usia dan jabatan // Ambil data dari tabel karyawans
$gajiKaryawan = GajiModel::whereNotNull('gaji') $gajiKaryawan = GajiModel::whereNotNull('gaji')
->where('gaji', '>', 0) ->where('gaji', '>', 0)
->select('id', 'nama', 'usia', 'jabatan', 'gaji', 'created_at') ->select('id', 'nama', 'tanggal_lahir', 'jabatan', 'gaji', 'created_at')
->get(); ->get()
->map(function ($karyawan) {
// Hitung usia dari timestamp
$usia = Carbon::createFromTimestamp($karyawan->tanggal_lahir)->age;
$karyawan->usia = $usia;
return $karyawan;
});
return view('Gaji', compact('gajiKaryawan')); return view('Gaji', compact('gajiKaryawan'));
} }
@ -22,12 +29,16 @@ public function index()
public function show($id) public function show($id)
{ {
$karyawan = GajiModel::findOrFail($id); $karyawan = GajiModel::findOrFail($id);
// Hitung usia untuk response
$karyawan->usia = Carbon::createFromTimestamp($karyawan->tanggal_lahir)->age;
return response()->json($karyawan); return response()->json($karyawan);
} }
public function edit($id) public function edit($id)
{ {
$karyawan = GajiModel::findOrFail($id); $karyawan = GajiModel::findOrFail($id);
// Konversi timestamp ke format date untuk form
$karyawan->tanggal_lahir = Carbon::createFromTimestamp($karyawan->tanggal_lahir)->format('Y-m-d');
return view('GajiEdit', compact('karyawan')); return view('GajiEdit', compact('karyawan'));
} }
@ -36,9 +47,13 @@ public function update(Request $request, $id)
try { try {
$karyawan = GajiModel::findOrFail($id); $karyawan = GajiModel::findOrFail($id);
// Konversi tanggal lahir ke timestamp
$tanggalLahir = Carbon::parse($request->tanggal_lahir)->timestamp;
// Bersihkan format angka dari gaji // Bersihkan format angka dari gaji
$request->merge([ $request->merge([
'gaji' => str_replace('.', '', $request->gaji) 'gaji' => str_replace('.', '', $request->gaji),
'tanggal_lahir' => $tanggalLahir
]); ]);
$karyawan->update($request->all()); $karyawan->update($request->all());

View File

@ -10,6 +10,7 @@
use Maatwebsite\Excel\Concerns\WithHeadings; use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping; use Maatwebsite\Excel\Concerns\WithMapping;
use Carbon\Carbon; use Carbon\Carbon;
use App\Http\Controllers\RiwayatController;
class LaporanController extends Controller class LaporanController extends Controller
{ {
@ -317,10 +318,34 @@ public function destroy($id)
{ {
try { try {
$laporan = LaporanModel::findOrFail($id); $laporan = LaporanModel::findOrFail($id);
// Catat riwayat sebelum menghapus
$riwayatController = new RiwayatController();
$keteranganRiwayat = "Menghapus laporan - Tanggal: " . date('d/m/Y', strtotime($laporan->Tanggal)) .
", Keterangan: " . $laporan->keterangan;
if (!empty($laporan->nama_karyawan)) {
$keteranganRiwayat .= ", Nama Karyawan: " . $laporan->nama_karyawan;
}
$riwayatController->store(
auth()->id(),
auth()->user()->nama,
'Hapus Laporan',
$keteranganRiwayat
);
$laporan->delete(); $laporan->delete();
return response()->json(['success' => true, 'message' => 'Data berhasil dihapus']);
return response()->json([
'success' => true,
'message' => 'Data berhasil dihapus'
]);
} catch (\Exception $e) { } catch (\Exception $e) {
return response()->json(['success' => false, 'message' => 'Gagal menghapus data: ' . $e->getMessage()], 500); return response()->json([
'success' => false,
'message' => 'Gagal menghapus data: ' . $e->getMessage()
], 500);
} }
} }
} }

View File

@ -0,0 +1,37 @@
<?php
namespace App\Http\Controllers;
use App\Models\Login;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
class RegisterController extends Controller
{
public function index()
{
if (Auth::check()) {
return redirect()->route('home');
}
return view('register');
}
public function store(Request $request)
{
$request->validate([
'nama' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:penggunas',
'password' => 'required|string|min:8|confirmed',
]);
$user = Login::create([
'nama' => $request->nama,
'email' => $request->email,
'password' => Hash::make($request->password),
'tipe_pengguna' => 'karyawan'
]);
return redirect()->route('login')->with('success', 'Registrasi berhasil! Silakan login dengan akun Anda.');
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace App\Http\Controllers;
use App\Models\RiwayatModel;
use Illuminate\Http\Request;
use Carbon\Carbon;
class RiwayatController extends Controller
{
public function index()
{
$riwayat = RiwayatModel::orderBy('tanggal', 'desc')
->orderBy('waktu', 'desc')
->get();
return view('Riwayat', compact('riwayat'));
}
public function store($userId, $namaUser, $aksi, $keterangan = '')
{
$now = Carbon::now();
RiwayatModel::create([
'user_id' => $userId,
'nama_user' => $namaUser,
'tanggal' => $now->format('Y-m-d'),
'waktu' => $now->format('H:i:s'),
'aksi' => $aksi,
'keterangan' => $keterangan
]);
}
}

View File

@ -4,6 +4,7 @@
use App\Models\UangMasukModel; // Import model use App\Models\UangMasukModel; // Import model
use App\Models\GajiModel; // Import model gaji use App\Models\GajiModel; // Import model gaji
use App\Models\LaporanModel; // Import model laporan
use Illuminate\Http\Request; use Illuminate\Http\Request;
class UangMasukController extends Controller class UangMasukController extends Controller
@ -28,12 +29,13 @@ public function store(Request $request)
// Debug untuk melihat data yang diterima // Debug untuk melihat data yang diterima
\Log::info('Request Data:', $request->all()); \Log::info('Request Data:', $request->all());
// Validasi input
$request->validate([ $request->validate([
'Tanggal' => 'required|date', 'Tanggal' => 'required|date',
'keterangan_type' => 'required|in:karyawan,manual', 'keterangan_type' => 'required|in:karyawan,manual',
'kategori' => 'required|array', 'kategori' => 'required|array',
'posisi' => 'required|array', 'posisi' => 'required|array',
'nominal' => 'required|array', 'nominal' => 'required|array'
]); ]);
// Gabungkan keterangan berdasarkan tipe input // Gabungkan keterangan berdasarkan tipe input
@ -47,12 +49,6 @@ public function store(Request $request)
if ($request->filled('keterangan_tambahan')) { if ($request->filled('keterangan_tambahan')) {
$keterangan .= ' - ' . $request->keterangan_tambahan; $keterangan .= ' - ' . $request->keterangan_tambahan;
} }
// Ambil gaji dari model GajiModel berdasarkan nama karyawan
$karyawan = GajiModel::where('nama', $request->keterangan)->first();
if (!$karyawan) {
throw new \Exception('Data karyawan tidak ditemukan');
}
} else { } else {
if (empty($request->keterangan_manual)) { if (empty($request->keterangan_manual)) {
throw new \Exception('Keterangan manual harus diisi'); throw new \Exception('Keterangan manual harus diisi');
@ -60,42 +56,52 @@ public function store(Request $request)
$keterangan = $request->keterangan_manual; $keterangan = $request->keterangan_manual;
} }
// Proses data
$data = [ $data = [
'Tanggal' => $request->Tanggal, 'Tanggal' => $request->Tanggal,
'keterangan' => $keterangan, 'keterangan' => $keterangan,
'nama_karyawan' => $request->keterangan_type === 'karyawan' ? $request->keterangan : null,
'kode' => $this->generateKode($request->kategori[0]),
'kategori' => $request->kategori[0],
'uang_masuk' => str_replace(['.', ','], '', $request->nominal[0]) ?? 0,
'uang_keluar' => 0
]; ];
// Proses setiap rekening // Tambahkan data untuk rekening tambahan jika ada
foreach ($request->kategori as $index => $kategori) { for ($i = 1; $i < count($request->kategori); $i++) {
$kode = $this->generateKode($kategori); if ($request->posisi[$i] === 'debit') {
$nominal = str_replace(['.', ','], '', $request->nominal[$index]); $data["uang_masuk" . ($i + 1)] = str_replace(['.', ','], '', $request->nominal[$i]);
$data["kode" . ($i + 1)] = $this->generateKode($request->kategori[$i]);
// Set kode dan kategori sesuai urutan $data["kategori" . ($i + 1)] = $request->kategori[$i];
$positionIndex = $index === 0 ? '' : ($index + 1);
$data["kode" . $positionIndex] = $kode;
$data["kategori" . $positionIndex] = $kategori;
// Set uang_masuk atau uang_keluar berdasarkan posisi
if ($request->posisi[$index] === 'debit') {
$data["uang_masuk" . $positionIndex] = $nominal;
$data["uang_keluar" . $positionIndex] = null;
} else { } else {
$data["uang_masuk" . $positionIndex] = null; $data["uang_keluar" . ($i + 1)] = str_replace(['.', ','], '', $request->nominal[$i]);
$data["uang_keluar" . $positionIndex] = $nominal; $data["kode" . ($i + 1)] = $this->generateKode($request->kategori[$i]);
$data["kategori" . ($i + 1)] = $request->kategori[$i];
} }
} }
// Debug untuk melihat data yang akan disimpan
\Log::info('Data to be saved:', $data);
// Simpan data // Simpan data
$result = UangMasukModel::create($data); $laporan = LaporanModel::create($data);
if (!$result) { // Catat riwayat
throw new \Exception('Gagal menyimpan data'); $riwayatController = new RiwayatController();
$keteranganRiwayat = "Input transaksi baru - Tanggal: " . date('d/m/Y', strtotime($request->Tanggal)) .
", Keterangan: " . $keterangan;
if ($request->keterangan_type === 'karyawan') {
$keteranganRiwayat .= ", Nama Karyawan: " . $request->keterangan;
} }
return redirect()->back()->with('success', 'Data berhasil disimpan!'); $keteranganRiwayat .= ", Kategori: " . implode(", ", array_filter($request->kategori));
$riwayatController->store(
auth()->id(),
auth()->user()->nama,
'Input Transaksi',
$keteranganRiwayat
);
return redirect()->back()->with('success', 'Data berhasil disimpan');
} catch (\Exception $e) { } catch (\Exception $e) {
\Log::error('Error in UangMasukController@store: ' . $e->getMessage()); \Log::error('Error in UangMasukController@store: ' . $e->getMessage());
return redirect()->back()->with('error', 'Gagal menyimpan data: ' . $e->getMessage()); return redirect()->back()->with('error', 'Gagal menyimpan data: ' . $e->getMessage());

View File

@ -10,7 +10,7 @@ class DataKaryawanModel extends Model
protected $fillable = [ protected $fillable = [
'nama', 'nama',
'usia', 'tanggal_lahir',
'jabatan', 'jabatan',
'gaji' 'gaji'
]; ];

View File

@ -24,7 +24,7 @@ class GajiModel extends Model
// Kolom yang dapat diisi // Kolom yang dapat diisi
protected $fillable = [ protected $fillable = [
'nama', 'nama',
'usia', 'tanggal_lahir',
'jabatan', 'jabatan',
'gaji' 'gaji'
]; ];
@ -38,5 +38,6 @@ class GajiModel extends Model
protected $casts = [ protected $casts = [
'gaji' => 'decimal:2', 'gaji' => 'decimal:2',
'tanggal_lahir' => 'integer'
]; ];
} }

View File

@ -3,11 +3,13 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class Login extends Model class Login extends Authenticatable
{ {
use HasFactory; use HasApiTokens, HasFactory, Notifiable;
protected $table = 'penggunas'; protected $table = 'penggunas';
@ -17,4 +19,14 @@ class Login extends Model
'password', 'password',
'tipe_pengguna' 'tipe_pengguna'
]; ];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
} }

View File

@ -0,0 +1,28 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class RiwayatModel extends Model
{
use HasFactory;
protected $table = 'riwayat';
protected $primaryKey = 'id';
protected $fillable = [
'user_id',
'nama_user',
'tanggal',
'waktu',
'aksi',
'keterangan'
];
// Relasi dengan model User
public function user()
{
return $this->belongsTo(Login::class, 'user_id');
}
}

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('riwayat', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('penggunas')->onDelete('cascade');
$table->string('nama_user');
$table->date('tanggal');
$table->time('waktu');
$table->string('aksi');
$table->text('keterangan')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('riwayat');
}
};

View File

@ -230,7 +230,12 @@
</a> </a>
</li> </li>
@endif @endif
<li class="mb-4">
<a href="{{ route('riwayat.index') }}" class="nav-link flex items-center p-2 rounded-lg hover:bg-blue-700 transition-colors">
<i class="fas fa-history mr-3"></i>
<span>Riwayat</span>
</a>
</li>
<li class="mt-8"> <li class="mt-8">
<form action="{{ route('logout') }}" method="POST" id="logout-form"> <form action="{{ route('logout') }}" method="POST" id="logout-form">
@csrf @csrf

View File

@ -26,20 +26,25 @@ class="w-full border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:
> >
</div> </div>
<!-- Usia --> <!-- Tanggal Lahir -->
<div class="form-group"> <div class="form-group">
<label class="block text-sm font-medium text-gray-700 mb-1"> <label class="block text-sm font-medium text-gray-700 mb-1">
Usia <span class="text-red-600">*</span> Tanggal Lahir <span class="text-red-600">*</span>
</label> </label>
<input <input
type="number" type="date"
name="usia" name="tanggal_lahir"
id="tanggal_lahir"
class="w-full border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" class="w-full border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Masukkan usia"
min="17"
max="65"
required required
max="{{ date('Y-m-d') }}"
onchange="hitungUsia(this.value)"
> >
<div class="mt-2">
<span class="text-sm font-medium text-gray-700">Usia: </span>
<span id="usia" class="text-sm text-gray-600">-</span>
<span class="text-sm text-gray-600">tahun</span>
</div>
<p class="text-xs text-gray-500 mt-1">Usia minimal 17 tahun dan maksimal 65 tahun</p> <p class="text-xs text-gray-500 mt-1">Usia minimal 17 tahun dan maksimal 65 tahun</p>
</div> </div>
@ -113,6 +118,72 @@ function formatNumber(input) {
input.value = value; input.value = value;
} }
function hitungUsia(tanggalLahir) {
const today = new Date();
const birthDate = new Date(tanggalLahir);
let usia = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
usia--;
}
// Validasi usia
if (usia < 17) {
Swal.fire({
icon: 'error',
title: 'Usia Tidak Memenuhi Syarat',
text: 'Usia minimal harus 17 tahun',
confirmButtonText: 'OK'
});
document.getElementById('tanggal_lahir').value = '';
document.getElementById('usia').textContent = '-';
return;
}
if (usia > 65) {
Swal.fire({
icon: 'error',
title: 'Usia Tidak Memenuhi Syarat',
text: 'Usia maksimal harus 65 tahun',
confirmButtonText: 'OK'
});
document.getElementById('tanggal_lahir').value = '';
document.getElementById('usia').textContent = '-';
return;
}
document.getElementById('usia').textContent = usia;
}
// Tambahkan validasi form sebelum submit
document.getElementById('karyawanForm').addEventListener('submit', function(e) {
const tanggalLahir = document.getElementById('tanggal_lahir').value;
if (!tanggalLahir) {
e.preventDefault();
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Silakan isi tanggal lahir',
confirmButtonText: 'OK'
});
return;
}
const usia = parseInt(document.getElementById('usia').textContent);
if (isNaN(usia) || usia < 17 || usia > 65) {
e.preventDefault();
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Usia harus antara 17-65 tahun',
confirmButtonText: 'OK'
});
return;
}
});
// Menampilkan notifikasi jika ada pesan sukses atau error // Menampilkan notifikasi jika ada pesan sukses atau error
@if(session('success')) @if(session('success'))
Swal.fire({ Swal.fire({

View File

@ -23,9 +23,19 @@ class="border rounded w-full py-2 px-3" required>
</div> </div>
<div class="mb-4"> <div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2">Usia</label> <label class="block text-gray-700 text-sm font-bold mb-2">Tanggal Lahir</label>
<input type="number" name="usia" value="{{ $karyawan->usia }}" <input type="date" name="tanggal_lahir" id="tanggal_lahir"
class="border rounded w-full py-2 px-3" required> value="{{ $karyawan->tanggal_lahir }}"
class="border rounded w-full py-2 px-3"
required
max="{{ date('Y-m-d') }}"
onchange="hitungUsia(this.value)">
<div class="mt-2">
<span class="text-sm font-medium text-gray-700">Usia: </span>
<span id="usia" class="text-sm text-gray-600">{{ Carbon\Carbon::createFromTimestamp($karyawan->tanggal_lahir)->age }}</span>
<span class="text-sm text-gray-600">tahun</span>
</div>
<p class="text-xs text-gray-500 mt-1">Usia minimal 17 tahun dan maksimal 65 tahun</p>
</div> </div>
<div class="mb-4"> <div class="mb-4">
@ -89,17 +99,56 @@ function formatNumber(input) {
input.value = value; input.value = value;
} }
function hitungUsia(tanggalLahir) {
const today = new Date();
const birthDate = new Date(tanggalLahir);
let usia = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
usia--;
}
// Validasi usia
if (usia < 17) {
Swal.fire({
icon: 'error',
title: 'Usia Tidak Memenuhi Syarat',
text: 'Usia minimal harus 17 tahun',
confirmButtonText: 'OK'
});
document.getElementById('tanggal_lahir').value = '';
document.getElementById('usia').textContent = '-';
return;
}
if (usia > 65) {
Swal.fire({
icon: 'error',
title: 'Usia Tidak Memenuhi Syarat',
text: 'Usia maksimal harus 65 tahun',
confirmButtonText: 'OK'
});
document.getElementById('tanggal_lahir').value = '';
document.getElementById('usia').textContent = '-';
return;
}
document.getElementById('usia').textContent = usia;
}
// Form submission handling // Form submission handling
document.getElementById('editGajiForm').addEventListener('submit', function(e) { document.getElementById('editGajiForm').addEventListener('submit', function(e) {
e.preventDefault(); e.preventDefault();
// Validasi form di sini jika diperlukan // Validasi form
const nama = document.querySelector('input[name="nama"]').value; const nama = document.querySelector('input[name="nama"]').value;
const usia = document.querySelector('input[name="usia"]').value; const tanggalLahir = document.querySelector('input[name="tanggal_lahir"]').value;
const jabatan = document.querySelector('input[name="jabatan"]').value; const jabatan = document.querySelector('input[name="jabatan"]').value;
const gaji = document.querySelector('input[name="gaji"]').value; const gaji = document.querySelector('input[name="gaji"]').value;
if (!nama || !usia || !jabatan || !gaji) { if (!nama || !tanggalLahir || !jabatan || !gaji) {
Swal.fire({ Swal.fire({
icon: 'error', icon: 'error',
title: 'Error!', title: 'Error!',
@ -109,6 +158,18 @@ function formatNumber(input) {
return; return;
} }
// Validasi usia
const usia = parseInt(document.getElementById('usia').textContent);
if (isNaN(usia) || usia < 17 || usia > 65) {
Swal.fire({
icon: 'error',
title: 'Error!',
text: 'Usia harus antara 17-65 tahun',
confirmButtonText: 'OK'
});
return;
}
// Submit form jika validasi berhasil // Submit form jika validasi berhasil
this.submit(); this.submit();
}); });

View File

@ -48,6 +48,10 @@
</div> </div>
<button type="submit" class="w-full py-2 mb-4 text-white bg-gradient-to-r from-teal-400 to-pink-500 rounded-lg hover:from-teal-500 hover:to-pink-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500">LOGIN</button> <button type="submit" class="w-full py-2 mb-4 text-white bg-gradient-to-r from-teal-400 to-pink-500 rounded-lg hover:from-teal-500 hover:to-pink-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500">LOGIN</button>
</form> </form>
<div class="text-center">
<p class="text-sm text-gray-600 mb-2">Belum punya akun?</p>
<a href="{{ route('register') }}" class="inline-block w-full py-2 text-white bg-gradient-to-r from-blue-400 to-purple-500 rounded-lg hover:from-blue-500 hover:to-purple-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">REGISTER</a>
</div>
<!-- <div class="text-center text-sm text-gray-600 mb-4">Or Sign Up Using</div> <!-- <div class="text-center text-sm text-gray-600 mb-4">Or Sign Up Using</div>
<div class="flex justify-center space-x-4"> <div class="flex justify-center space-x-4">
<a href="#" class="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center text-white"> <a href="#" class="w-8 h-8 bg-blue-600 rounded-full flex items-center justify-center text-white">
@ -92,5 +96,19 @@
}); });
</script> </script>
@endif @endif
@if(session('success'))
<script>
document.addEventListener('DOMContentLoaded', function() {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: '{{ session("success") }}',
confirmButtonColor: '#3085d6',
confirmButtonText: 'OK'
});
});
</script>
@endif
</body> </body>
</html> </html>

View File

@ -0,0 +1,381 @@
@extends('Core.Sidebar')
@section('content')
<title>Riwayat Aktivitas</title>
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<style>
/* Gradient Background */
body {
background: #f3f4f6;
}
/* Card Styling */
.box {
background: white;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
/* Table Styling */
.print-table {
border-collapse: collapse;
width: 100%;
}
.print-table th {
background-color: #f9fafb;
font-weight: 600;
text-transform: uppercase;
font-size: 0.875rem;
padding: 0.75rem 1rem;
border-bottom: 2px solid #e5e7eb;
}
.print-table td {
padding: 0.75rem 1rem;
border-bottom: 1px solid #e5e7eb;
}
.print-table tbody tr:hover {
background-color: #f9fafb;
transition: all 0.3s ease;
}
/* Filter Controls */
.filter-controls {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.filter-group {
display: flex;
align-items: center;
gap: 0.5rem;
}
.filter-input {
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 0.5rem;
min-width: 200px;
}
.filter-input:focus {
outline: none;
border-color: #3b82f6;
ring: 2px;
ring-color: #93c5fd;
}
/* Action Badge */
.action-badge {
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
}
.action-delete {
background-color: #fee2e2;
color: #dc2626;
}
.action-edit {
background-color: #dbeafe;
color: #2563eb;
}
.action-create {
background-color: #dcfce7;
color: #16a34a;
}
/* Responsive Design */
@media (max-width: 640px) {
.filter-controls {
flex-direction: column;
}
.filter-group {
width: 100%;
}
.filter-input {
width: 100%;
}
}
/* Keterangan Styling */
.keterangan {
max-width: 300px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.keterangan:hover {
white-space: normal;
overflow: visible;
position: relative;
z-index: 1;
background: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 0.5rem;
border-radius: 0.25rem;
}
/* Pagination Controls */
.pagination-controls {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
margin-top: 1rem;
}
.pagination-controls button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination-controls button:hover:not(:disabled) {
background-color: #e5e7eb;
}
.rows-per-page {
display: flex;
align-items: center;
gap: 0.5rem;
}
.rows-per-page select {
border: 1px solid #e5e7eb;
border-radius: 0.375rem;
padding: 0.5rem;
font-size: 0.875rem;
}
.rows-per-page select:focus {
outline: none;
border-color: #3b82f6;
ring: 2px;
ring-color: #93c5fd;
}
</style>
<div class="box p-4 intro-y mt-5">
<div class="intro-y">
<!-- Header -->
<div class="mb-4 bg-blue-600 text-white p-4 rounded-lg shadow-md">
<h1 class="text-2xl font-bold">Riwayat Aktivitas</h1>
<p class="text-sm mt-1">Halaman ini menampilkan riwayat aktivitas pengguna dalam sistem.</p>
</div>
<!-- Filter -->
<div class="filter-controls">
<div class="filter-group">
<label for="searchRiwayat" class="font-medium">Cari:</label>
<input type="text" id="searchRiwayat" class="filter-input" placeholder="Cari nama/aksi...">
</div>
<div class="filter-group">
<label for="dateFilter" class="font-medium">Tanggal:</label>
<input type="date" id="dateFilter" class="filter-input">
</div>
</div>
<!-- Tabel Riwayat -->
<div class="overflow-x-auto">
<table class="print-table">
<thead>
<tr>
<th class="text-left">No</th>
<th class="text-left">Nama User</th>
<th class="text-left">Tanggal</th>
<th class="text-left">Waktu (WIB)</th>
<th class="text-left">Aksi</th>
<th class="text-left">Keterangan</th>
</tr>
</thead>
<tbody>
@foreach($riwayat as $index => $item)
<tr>
<td>{{ $index + 1 }}</td>
<td>{{ $item->nama_user }}</td>
<td>{{ date('d/m/Y', strtotime($item->tanggal)) }}</td>
<td>{{ \Carbon\Carbon::parse($item->waktu)->setTimezone('Asia/Jakarta')->format('H:i:s') }} WIB</td>
<td>
<span class="action-badge
@if(str_contains(strtolower($item->aksi), 'hapus')) action-delete
@elseif(str_contains(strtolower($item->aksi), 'edit')) action-edit
@else action-create
@endif">
{{ $item->aksi }}
</span>
</td>
<td class="keterangan" title="{{ $item->keterangan }}">{{ $item->keterangan }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Pagination Controls -->
<div class="intro-y col-span-12 flex flex-wrap sm:flex-row sm:flex-nowrap items-center py-5 px-4 bg-white rounded-md shadow-md mt-4">
<!-- Filter Rows per Page -->
<div class="flex items-center space-x-2 flex-grow">
<label for="rowsPerPage" class="text-sm font-medium">Rows per page:</label>
<select id="rowsPerPage" class="border rounded-md py-3 px-6 ml-1 text-sm focus:outline-none focus:ring focus:border-blue-300">
<option value="5" selected>5</option>
<option value="10">10</option>
<option value="15">15</option>
<option value="all">All</option>
</select>
</div>
<!-- Pagination Controls -->
<div class="flex items-center space-x-4 ml-2">
<button id="prevPage" class="text-sm px-3 py-2 border rounded-md bg-gray-100 hover:bg-gray-200 focus:outline-none">Previous</button>
<span id="pageIndicator" class="text-sm font-medium">Page 1</span>
<button id="nextPage" class="text-sm px-3 py-2 border rounded-md bg-gray-100 hover:bg-gray-200 focus:outline-none">Next</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('searchRiwayat');
const dateFilter = document.getElementById('dateFilter');
const tableRows = document.querySelectorAll('tbody tr');
const rowsPerPageSelect = document.getElementById('rowsPerPage');
const prevButton = document.getElementById('prevPage');
const nextButton = document.getElementById('nextPage');
const pageIndicator = document.getElementById('pageIndicator');
let currentPage = 1;
let rowsPerPage = parseInt(rowsPerPageSelect.value);
function displayTableRows() {
const start = (currentPage - 1) * rowsPerPage;
const end = start + rowsPerPage;
let visibleRows = 0;
// Sembunyikan semua baris terlebih dahulu
Array.from(tableRows).forEach((row, index) => {
if (index >= start && index < end) {
row.style.display = '';
visibleRows++;
} else {
row.style.display = 'none';
}
});
// Update page indicator
const totalPages = Math.ceil(tableRows.length / rowsPerPage);
pageIndicator.textContent = `Page ${currentPage} of ${totalPages}`;
// Update button states
prevButton.disabled = currentPage === 1;
nextButton.disabled = currentPage >= totalPages;
// Tambahkan class untuk styling button disabled
if (prevButton.disabled) {
prevButton.classList.add('opacity-50', 'cursor-not-allowed');
} else {
prevButton.classList.remove('opacity-50', 'cursor-not-allowed');
}
if (nextButton.disabled) {
nextButton.classList.add('opacity-50', 'cursor-not-allowed');
} else {
nextButton.classList.remove('opacity-50', 'cursor-not-allowed');
}
}
function filterTable() {
const searchTerm = searchInput.value.toLowerCase();
const selectedDate = dateFilter.value;
tableRows.forEach(row => {
const namaUser = row.cells[1].textContent.toLowerCase();
const aksi = row.cells[4].textContent.toLowerCase();
const tanggal = row.cells[2].textContent;
const rowDate = tanggal.split('/').reverse().join('-');
const matchesSearch = namaUser.includes(searchTerm) || aksi.includes(searchTerm);
const matchesDate = !selectedDate || rowDate === selectedDate;
row.style.display = matchesSearch && matchesDate ? '' : 'none';
});
// Reset ke halaman pertama setelah filter
currentPage = 1;
displayTableRows();
}
// Event listener untuk rows per page
rowsPerPageSelect.addEventListener('change', function() {
if (this.value === 'all') {
rowsPerPage = tableRows.length;
} else {
rowsPerPage = parseInt(this.value);
}
currentPage = 1;
displayTableRows();
// Simpan preferensi di localStorage
localStorage.setItem('preferredRowsPerPage', this.value);
});
// Event listener untuk previous button
prevButton.addEventListener('click', function() {
if (currentPage > 1) {
currentPage--;
displayTableRows();
}
});
// Event listener untuk next button
nextButton.addEventListener('click', function() {
if (currentPage < Math.ceil(tableRows.length / rowsPerPage)) {
currentPage++;
displayTableRows();
}
});
// Event listener untuk pencarian dan filter tanggal
searchInput.addEventListener('input', filterTable);
dateFilter.addEventListener('change', filterTable);
// Load saved preference dari localStorage
const savedRowsPerPage = localStorage.getItem('preferredRowsPerPage');
if (savedRowsPerPage) {
rowsPerPageSelect.value = savedRowsPerPage;
if (savedRowsPerPage === 'all') {
rowsPerPage = tableRows.length;
} else {
rowsPerPage = parseInt(savedRowsPerPage);
}
}
// Keyboard navigation
document.addEventListener('keydown', function(e) {
if (e.key === 'ArrowLeft' && !prevButton.disabled) {
prevButton.click();
} else if (e.key === 'ArrowRight' && !nextButton.disabled) {
nextButton.click();
}
});
// Initial display
displayTableRows();
});
</script>
@endsection

View File

@ -25,6 +25,8 @@
name="Tanggal" name="Tanggal"
class="w-full border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" class="w-full border rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
required required
min="{{ date('Y-m-d') }}"
value="{{ date('Y-m-d') }}"
> >
</div> </div>

View File

@ -9,6 +9,11 @@
<!-- Font Awesome --> <!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<!-- Tambahkan variabel PHP untuk tipe pengguna -->
@php
$isOwner = auth()->user()->tipe_pengguna === 'owner';
@endphp
<style> <style>
/* Gradient Background */ /* Gradient Background */
body { body {
@ -136,10 +141,12 @@
</div> </div>
<div class="filter-group ml-auto"> <div class="filter-group ml-auto">
<a href="{{ route('User.create') }}" id="createUser" class="btn btn-primary"> @if($isOwner)
<i class="fas fa-plus"></i> <a href="{{ route('User.create') }}" id="createUser" class="btn btn-primary">
<span>Tambah User</span> <i class="fas fa-plus"></i>
</a> <span>Tambah User</span>
</a>
@endif
</div> </div>
</div> </div>
@ -164,6 +171,9 @@
</div> </div>
<script> <script>
// Tambahkan variabel JavaScript untuk tipe pengguna
const isOwner = @json($isOwner);
// Notifikasi SweetAlert2 // Notifikasi SweetAlert2
@if(session('success')) @if(session('success'))
Swal.fire({ Swal.fire({
@ -215,12 +225,14 @@ function loadUsers(typeFilter = '', searchFilter = '') {
<td>${user.email}</td> <td>${user.email}</td>
<td>${user.tipe_pengguna}</td> <td>${user.tipe_pengguna}</td>
<td class="text-center"> <td class="text-center">
<a href="/User/${user.id}/edit" class="text-blue-600 hover:text-blue-800 mx-1"> ${isOwner ? `
<i class="fas fa-edit"></i> <a href="/User/${user.id}/edit" class="text-blue-600 hover:text-blue-800 mx-1">
</a> <i class="fas fa-edit"></i>
<button onclick="deleteUser(${user.id})" class="text-red-600 hover:text-red-800 mx-1"> </a>
<i class="fas fa-trash"></i> <button onclick="deleteUser(${user.id})" class="text-red-600 hover:text-red-800 mx-1">
</button> <i class="fas fa-trash"></i>
</button>
` : ''}
</td> </td>
`; `;
tbody.appendChild(row); tbody.appendChild(row);
@ -238,6 +250,16 @@ function loadUsers(typeFilter = '', searchFilter = '') {
} }
function deleteUser(id) { function deleteUser(id) {
if (!isOwner) {
Swal.fire({
icon: 'error',
title: 'Akses Ditolak!',
text: 'Anda tidak memiliki akses untuk menghapus user',
confirmButtonText: 'OK'
});
return;
}
Swal.fire({ Swal.fire({
title: 'Apakah Anda yakin?', title: 'Apakah Anda yakin?',
text: "Data yang dihapus tidak dapat dikembalikan!", text: "Data yang dihapus tidak dapat dikembalikan!",

View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Register</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container">
<div class="row justify-content-center mt-5">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h3 class="text-center">Register</h3>
</div>
<div class="card-body">
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('register.store') }}" method="POST">
@csrf
<div class="mb-3">
<label for="nama" class="form-label">Nama</label>
<input type="text" class="form-control" id="nama" name="nama" value="{{ old('nama') }}" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" value="{{ old('email') }}" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="password_confirmation" class="form-label">Konfirmasi Password</label>
<input type="password" class="form-control" id="password_confirmation" name="password_confirmation" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</form>
<div class="text-center mt-3">
<p>Sudah punya akun? <a href="{{ route('login') }}">Login di sini</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -17,6 +17,8 @@
use App\Http\Controllers\RekeningController; use App\Http\Controllers\RekeningController;
use App\Http\Controllers\NeracasaldoController; use App\Http\Controllers\NeracasaldoController;
use App\Http\Controllers\LabarugiController; use App\Http\Controllers\LabarugiController;
use App\Http\Controllers\RegisterController;
use App\Http\Controllers\RiwayatController;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Web Routes | Web Routes
@ -87,4 +89,10 @@
Route::get('/labarugi/filter', [LabarugiController::class, 'filter'])->name('labarugi.filter'); Route::get('/labarugi/filter', [LabarugiController::class, 'filter'])->name('labarugi.filter');
Route::get('/labarugi/export-excel', [LabarugiController::class, 'exportExcel'])->name('labarugi.export-excel'); Route::get('/labarugi/export-excel', [LabarugiController::class, 'exportExcel'])->name('labarugi.export-excel');
Route::get('/labarugi/export-pdf', [LabarugiController::class, 'exportPDF'])->name('labarugi.export-pdf'); Route::get('/labarugi/export-pdf', [LabarugiController::class, 'exportPDF'])->name('labarugi.export-pdf');
// Route untuk Riwayat
Route::get('/riwayat', [RiwayatController::class, 'index'])->name('riwayat.index');
}); });
Route::get('/register', [RegisterController::class, 'index'])->name('register');
Route::post('/register', [RegisterController::class, 'store'])->name('register.store');