This commit is contained in:
Dimas 2025-06-25 14:25:24 +07:00
parent 981dbe4524
commit 53fe6c3220
38 changed files with 1844 additions and 276 deletions

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class ForgotPasswordController extends Controller
{
public function showLinkRequestForm()
{
return view('forgotPassword');
}
public function sendResetLinkEmail(Request $request)
{
$request->validate(['email' => 'required|email']);
$status = Password::sendResetLink(
$request->only('email')
);
return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class ResetPasswordController extends Controller
{
public function showResetForm(Request $request, $token)
{
return view('resetPassword', ['token' => $token, 'email' => $request->email]);
}
public function reset(Request $request)
{
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => 'required|confirmed|min:8',
]);
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($user, $password) {
$user->forceFill([
'password' => Hash::make($password),
'remember_token' => Str::random(60),
])->save();
}
);
return $status === Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withErrors(['email' => [__($status)]]);
}
}

View File

@ -20,11 +20,11 @@ class DashboardController extends Controller
public function pimpinan(Request $request)
{
$currentYear = now()->year;
$start = $request->get('start_date')
? Carbon::parse($request->get('start_date'))
$start = $request->get('start_date')
? Carbon::parse($request->get('start_date'))
: Carbon::create($currentYear, 1, 1)->startOfDay();
$end = $request->get('end_date')
? Carbon::parse($request->get('end_date'))
$end = $request->get('end_date')
? Carbon::parse($request->get('end_date'))
: Carbon::create($currentYear, 12, 31)->endOfDay();
$kloters = HistoryGajiKloter::whereNotNull('tanggal_awal')
@ -32,10 +32,58 @@ public function pimpinan(Request $request)
->orderBy('id', 'desc')
->get();
$query = Transaksi::whereBetween('waktu_transaksi', [$start, $end]);
$kloterId = $request->get('kloter');
$labelsKloter = [];
$pendapatanKloter = [];
$pengeluaranKloter = [];
if ($kloterId) {
$selectedKloter = HistoryGajiKloter::find($kloterId);
if ($selectedKloter) {
$labelsKloter[] = $selectedKloter->nama ?? 'Kloter ' . $selectedKloter->id;
$pendapatanKloter[] = Transaksi::whereBetween('waktu_transaksi', [
$selectedKloter->tanggal_awal,
$selectedKloter->tanggal_akhir
])
->whereNotNull('pemasukan_id')
->sum('jumlahRp');
$pengeluaranKloter[] = Transaksi::whereBetween('waktu_transaksi', [
$selectedKloter->tanggal_awal,
$selectedKloter->tanggal_akhir
])
->where(function ($q) {
$q->whereNotNull('pengeluaran_id')
->orWhereNotNull('history_gaji_kloter_id');
})
->sum('jumlahRp');
}
} else {
foreach ($kloters as $kloter) {
$labelsKloter[] = $kloter->nama ?? 'Kloter ' . $kloter->id;
$pendapatanKloter[] = Transaksi::whereBetween('waktu_transaksi', [
$kloter->tanggal_awal,
$kloter->tanggal_akhir
])
->whereNotNull('pemasukan_id')
->sum('jumlahRp');
$pengeluaranKloter[] = Transaksi::whereBetween('waktu_transaksi', [
$kloter->tanggal_awal,
$kloter->tanggal_akhir
])
->where(function ($q) {
$q->whereNotNull('pengeluaran_id')
->orWhereNotNull('history_gaji_kloter_id');
})
->sum('jumlahRp');
}
}
$dates = CarbonPeriod::create($start, $end);
$labels = [];
$pendapatanBulanan = [];
$pengeluaranBulanan = [];
@ -43,19 +91,16 @@ public function pimpinan(Request $request)
foreach ($dates as $date) {
$labels[] = $date->format('d M');
$pendapatan = Transaksi::whereDate('waktu_transaksi', $date)
$pendapatanBulanan[] = Transaksi::whereDate('waktu_transaksi', $date)
->whereNotNull('pemasukan_id')
->sum('jumlahRp');
$pengeluaran = Transaksi::whereDate('waktu_transaksi', $date)
$pengeluaranBulanan[] = Transaksi::whereDate('waktu_transaksi', $date)
->where(function ($q) {
$q->whereNotNull('pengeluaran_id')
->orWhereNotNull('history_gaji_kloter_id');
->orWhereNotNull('history_gaji_kloter_id');
})
->sum('jumlahRp');
$pendapatanBulanan[] = $pendapatan;
$pengeluaranBulanan[] = $pengeluaran;
}
$bulanAktif = $start->translatedFormat('d M Y') . ' - ' . $end->translatedFormat('d M Y');
@ -67,7 +112,7 @@ public function pimpinan(Request $request)
$totalPengeluaran = Transaksi::whereBetween('waktu_transaksi', [$start, $end])
->where(function ($q) {
$q->whereNotNull('pengeluaran_id')
->orWhereNotNull('history_gaji_kloter_id');
->orWhereNotNull('history_gaji_kloter_id');
})
->sum('jumlahRp');
@ -76,13 +121,28 @@ public function pimpinan(Request $request)
'pengeluaran' => $totalPengeluaran
];
/**
* Mengambil seluruh data dari tabel HistoryGajiKloter dan mengurutkannya berdasarkan kolom 'id' secara menurun (desc).
*
* Fungsi kode ini adalah untuk mendapatkan daftar kloter gaji terbaru terlebih dahulu,
* sehingga data yang paling baru akan berada di urutan teratas pada hasil query.
*
* @var \Illuminate\Database\Eloquent\Collection $listKloter Koleksi data kloter gaji yang telah diurutkan.
*/
$listKloter = HistoryGajiKloter::orderBy('id', 'asc')->get();
// $listKloter = HistoryGajiKloter::orderBy('id', 'desc')->get();
return view('dashboard.pimpinan', compact(
'labels',
'pendapatanBulanan',
'pengeluaranBulanan',
'keuangan',
'labels',
'pendapatanBulanan',
'pengeluaranBulanan',
'keuangan',
'bulanAktif',
'kloters'
'kloters',
'listKloter',
'labelsKloter',
'pendapatanKloter',
'pengeluaranKloter'
));
}

View File

@ -198,7 +198,6 @@ public function kloterSelesai($id)
return redirect()->route('gaji.kloter')->with('success', 'Kloter berhasil diselesaikan dan transaksi gaji dicatat.');
}
public function export($id)
{
$kloter = \App\Models\Kloter::with('tonIkan')->findOrFail($id);

View File

@ -28,7 +28,8 @@ public function barang(Request $request)
$filter = $request->query('filter');
$nama = $request->query('nama');
$isAjax = $request->ajax();
$isExport = $request->query('export') === 'excel';
$isExportExcel = $request->query('export') === 'excel';
$isExportPdf = $request->query('export') === 'pdf';
$barangs = Barang::with(['produk', 'pendukung']);
@ -50,10 +51,20 @@ public function barang(Request $request)
}
})->values();
if ($isExport) {
if ($isExportExcel) {
return Excel::download(new LaporanBarangExport($barangs), 'laporan-barang.xlsx');
}
if ($isExportPdf) {
$pdf = PDF::loadView('pimpinan.laporan_barang.pdf', [
'barangs' => $barangs,
'filter' => $filter,
'nama' => $nama
])->setPaper('a4', 'landscape');
return $pdf->download('laporan-barang.pdf');
}
if ($isAjax) {
return view('pimpinan.laporan_barang._table', compact('barangs'))->render();
}
@ -201,6 +212,17 @@ public function karyawan(Request $request)
return Excel::download(new LaporanKaryawanExport($data), 'laporan_karyawan.xlsx');
}
if ($request->has('export') && $request->export === 'pdf') {
$pdf = Pdf::loadView('pimpinan.laporan_karyawan.pdf', [
'data' => $data,
'filter' => $filter,
'nama' => $nama
])->setPaper('a4', 'landscape');
return $pdf->download('laporan_karyawan.pdf');
}
return view('pimpinan.laporan_karyawan.index', compact('data', 'filter', 'nama'));
}
@ -215,7 +237,6 @@ public function supplier(Request $request)
});
}
if ($request->filled('kategori')) {
if ($request->kategori === 'pemasok') {
$query->whereHas('pemasok');
@ -229,6 +250,16 @@ public function supplier(Request $request)
if ($request->has('export') && $request->export === 'excel') {
return Excel::download(new LaporanSupplierExport($suppliers), 'laporan_supplier.xlsx');
}
if ($request->has('export') && $request->export === 'pdf') {
$pdf = Pdf::loadView('pimpinan.laporan_supplier.pdf', [
'suppliers' => $suppliers,
'kategori' => $request->kategori,
'keyword' => $request->keyword
])->setPaper('a4', 'landscape');
return $pdf->download('laporan_supplier.pdf');
}
if ($request->ajax()) {
return view('pimpinan.laporan_supplier._table', compact('suppliers'))->render();
@ -267,10 +298,18 @@ public function transaksi(Request $request)
}
// Filter tanggal jika ada input
if ($request->filled('tanggal_mulai') && $request->filled('tanggal_akhir')) {
$tanggalMulai = null;
$tanggalAkhir = null;
if ($request->filled('daterange')) {
[$tanggalMulai, $tanggalAkhir] = explode(' - ', $request->daterange);
// Ubah ke format YYYY-MM-DD
$tanggalMulai = Carbon::createFromFormat('d-m-Y', trim($tanggalMulai))->format('Y-m-d');
$tanggalAkhir = Carbon::createFromFormat('d-m-Y', trim($tanggalAkhir))->format('Y-m-d');
$query->whereBetween('waktu_transaksi', [
$request->tanggal_mulai . ' 00:00:00',
$request->tanggal_akhir . ' 23:59:59'
$tanggalMulai . ' 00:00:00',
$tanggalAkhir . ' 23:59:59'
]);
}
@ -297,13 +336,13 @@ public function transaksi(Request $request)
'keluar' => $keluar,
'total' => $totalSekarang,
];
})->reverse()->values();
})->values();
if ($request->has('export')) {
if ($request->export === 'excel') {
return Excel::download(new LaporanTransaksiExport(
$request->tanggal_mulai,
$request->tanggal_akhir,
$tanggalMulai,
$tanggalAkhir,
$request->q
), 'laporan_transaksi.xlsx');
}
@ -317,10 +356,10 @@ public function transaksi(Request $request)
}
if ($request->ajax()) {
return view('pimpinan.laporan_transaksi._table', compact('data'));
return view('pimpinan.laporan_transaksi._table', compact('data'))->render();
}
return view('pimpinan.laporan_transaksi.index', compact('data'));
return view('pimpinan.laporan_transaksi.index', compact('data', 'tanggalMulai', 'tanggalAkhir'));
}
}

View File

@ -158,4 +158,32 @@ public function pilihKaryawan(Request $request)
->with('success', 'Karyawan berhasil dipilih untuk kloter ini.');
}
public function updateJamMasukAjax(Request $request)
{
$request->validate([
'id' => 'required|exists:presensis,id',
'jam_masuk' => 'required|date_format:H:i',
]);
$presensi = Presensi::find($request->id);
$presensi->jam_masuk = $request->jam_masuk;
$presensi->save();
return response()->json(['message' => 'Berhasil disimpan']);
}
public function updateJamPulangAjax(Request $request)
{
$request->validate([
'id' => 'required|exists:presensis,id',
'jam_pulang' => 'required|date_format:H:i',
]);
$presensi = Presensi::find($request->id);
$presensi->jam_pulang = $request->jam_pulang;
$presensi->save();
return response()->json(['message' => 'Berhasil disimpan']);
}
}

View File

@ -15,6 +15,7 @@
use App\Exports\LaporanTransaksiExport;
use Maatwebsite\Excel\Facades\Excel;
use Barryvdh\DomPDF\Facade\Pdf;
class TransaksiController extends Controller
{
@ -363,5 +364,24 @@ public function exportExcel(Request $request)
return Excel::download(new LaporanTransaksiExport($tanggalMulai, $tanggalAkhir, $q), $filename);
}
public function exportPDF(Request $request)
{
$tanggalMulai = $request->tanggal_mulai;
$tanggalAkhir = $request->tanggal_akhir;
$q = $request->q;
// Ambil data transaksi dengan class export
$export = new LaporanTransaksiExport($tanggalMulai, $tanggalAkhir, $q);
$data = $export->collection(); // Ini sudah dalam format collection map
$pdf = Pdf::loadView('operator.transaksi.pdf', [
'data' => $data,
'tanggalMulai' => $tanggalMulai,
'tanggalAkhir' => $tanggalAkhir,
'q' => $q
])->setPaper('a4', 'landscape');
return $pdf->download('laporan_transaksi_operator_' . now()->format('Ymd_His') . '.pdf');
}
}

18
app/Mail/TestMail.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class TestMail extends Mailable
{
use Queueable, SerializesModels;
public function build()
{
return $this->subject('Tes Kirim Email')
->view('emails.test');
}
}

View File

@ -6,11 +6,14 @@
"license": "MIT",
"require": {
"php": "^8.1",
"barryvdh/laravel-dompdf": "^3.1",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.10",
"laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8",
"maatwebsite/excel": "^3.1"
"maatwebsite/excel": "^3.1",
"symfony/http-client": "^7.3",
"symfony/mailgun-mailer": "^7.1"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",

608
composer.lock generated
View File

@ -4,8 +4,85 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "cf129d58fa9a70571645e78a68230150",
"content-hash": "d3041b4e1e7182f72b945efde6d8b976",
"packages": [
{
"name": "barryvdh/laravel-dompdf",
"version": "v3.1.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-dompdf.git",
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
"shasum": ""
},
"require": {
"dompdf/dompdf": "^3.0",
"illuminate/support": "^9|^10|^11|^12",
"php": "^8.1"
},
"require-dev": {
"larastan/larastan": "^2.7|^3.0",
"orchestra/testbench": "^7|^8|^9|^10",
"phpro/grumphp": "^2.5",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"PDF": "Barryvdh\\DomPDF\\Facade\\Pdf",
"Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf"
},
"providers": [
"Barryvdh\\DomPDF\\ServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Barryvdh\\DomPDF\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "A DOMPDF Wrapper for Laravel",
"keywords": [
"dompdf",
"laravel",
"pdf"
],
"support": {
"issues": "https://github.com/barryvdh/laravel-dompdf/issues",
"source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.1"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2025-02-13T15:07:54+00:00"
},
{
"name": "brick/math",
"version": "0.12.3",
@ -538,6 +615,161 @@
],
"time": "2024-02-05T11:56:58+00:00"
},
{
"name": "dompdf/dompdf",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
"reference": "a51bd7a063a65499446919286fb18b518177155a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/a51bd7a063a65499446919286fb18b518177155a",
"reference": "a51bd7a063a65499446919286fb18b518177155a",
"shasum": ""
},
"require": {
"dompdf/php-font-lib": "^1.0.0",
"dompdf/php-svg-lib": "^1.0.0",
"ext-dom": "*",
"ext-mbstring": "*",
"masterminds/html5": "^2.0",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"ext-gd": "*",
"ext-json": "*",
"ext-zip": "*",
"mockery/mockery": "^1.3",
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0"
},
"suggest": {
"ext-gd": "Needed to process images",
"ext-gmagick": "Improves image processing performance",
"ext-imagick": "Improves image processing performance",
"ext-zlib": "Needed for pdf stream compression"
},
"type": "library",
"autoload": {
"psr-4": {
"Dompdf\\": "src/"
},
"classmap": [
"lib/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "The Dompdf Community",
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
}
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
"source": "https://github.com/dompdf/dompdf/tree/v3.1.0"
},
"time": "2025-01-15T14:09:04+00:00"
},
{
"name": "dompdf/php-font-lib",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-font-lib.git",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6"
},
"type": "library",
"autoload": {
"psr-4": {
"FontLib\\": "src/FontLib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "The FontLib Community",
"homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/dompdf/php-font-lib",
"support": {
"issues": "https://github.com/dompdf/php-font-lib/issues",
"source": "https://github.com/dompdf/php-font-lib/tree/1.0.1"
},
"time": "2024-12-02T14:37:59+00:00"
},
{
"name": "dompdf/php-svg-lib",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-svg-lib.git",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0",
"sabberworm/php-css-parser": "^8.4"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Svg\\": "src/Svg"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The SvgLib Community",
"homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/dompdf/php-svg-lib",
"support": {
"issues": "https://github.com/dompdf/php-svg-lib/issues",
"source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0"
},
"time": "2024-04-29T13:26:35+00:00"
},
{
"name": "dragonmantank/cron-expression",
"version": "v3.4.0",
@ -2376,6 +2608,73 @@
},
"time": "2022-12-02T22:17:43+00:00"
},
{
"name": "masterminds/html5",
"version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
"reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
"reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Masterminds\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Butcher",
"email": "technosophos@gmail.com"
},
{
"name": "Matt Farina",
"email": "matt@mattfarina.com"
},
{
"name": "Asmir Mustafic",
"email": "goetas@gmail.com"
}
],
"description": "An HTML5 parser and serializer.",
"homepage": "http://masterminds.github.io/html5-php",
"keywords": [
"HTML5",
"dom",
"html",
"parser",
"querypath",
"serializer",
"xml"
],
"support": {
"issues": "https://github.com/Masterminds/html5-php/issues",
"source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
},
"time": "2024-03-31T07:05:07+00:00"
},
{
"name": "monolog/monolog",
"version": "3.9.0",
@ -3748,6 +4047,71 @@
},
"time": "2025-06-01T06:28:46+00:00"
},
{
"name": "sabberworm/php-css-parser",
"version": "v8.8.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
"reference": "3de493bdddfd1f051249af725c7e0d2c38fed740"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/3de493bdddfd1f051249af725c7e0d2c38fed740",
"reference": "3de493bdddfd1f051249af725c7e0d2c38fed740",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "9.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Sabberworm\\CSS\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raphael Schweikert"
},
{
"name": "Oliver Klee",
"email": "github@oliverklee.de"
},
{
"name": "Jake Hotson",
"email": "jake.github@qzdesign.co.uk"
}
],
"description": "Parser for CSS Files written in PHP",
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
"keywords": [
"css",
"parser",
"stylesheet"
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.8.0"
},
"time": "2025-03-23T17:59:05+00:00"
},
{
"name": "symfony/console",
"version": "v6.4.22",
@ -4269,6 +4633,179 @@
],
"time": "2024-12-29T13:51:37+00:00"
},
{
"name": "symfony/http-client",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "57e4fb86314015a695a750ace358d07a7e37b8a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/57e4fb86314015a695a750ace358d07a7e37b8a9",
"reference": "57e4fb86314015a695a750ace358d07a7e37b8a9",
"shasum": ""
},
"require": {
"php": ">=8.2",
"psr/log": "^1|^2|^3",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/http-client-contracts": "~3.4.4|^3.5.2",
"symfony/service-contracts": "^2.5|^3"
},
"conflict": {
"amphp/amp": "<2.5",
"php-http/discovery": "<1.15",
"symfony/http-foundation": "<6.4"
},
"provide": {
"php-http/async-client-implementation": "*",
"php-http/client-implementation": "*",
"psr/http-client-implementation": "1.0",
"symfony/http-client-implementation": "3.0"
},
"require-dev": {
"amphp/http-client": "^4.2.1|^5.0",
"amphp/http-tunnel": "^1.0|^2.0",
"amphp/socket": "^1.1",
"guzzlehttp/promises": "^1.4|^2.0",
"nyholm/psr7": "^1.0",
"php-http/httplug": "^1.0|^2.0",
"psr/http-client": "^1.0",
"symfony/amphp-http-client-meta": "^1.0|^2.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/messenger": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/rate-limiter": "^6.4|^7.0",
"symfony/stopwatch": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
"homepage": "https://symfony.com",
"keywords": [
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.3.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-05-02T08:23:16+00:00"
},
{
"name": "symfony/http-client-contracts",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
"reference": "75d7043853a42837e68111812f4d964b01e5101c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c",
"reference": "75d7043853a42837e68111812f4d964b01e5101c",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/contracts",
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\HttpClient\\": ""
},
"exclude-from-classmap": [
"/Test/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to HTTP clients",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"support": {
"source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-04-29T11:18:49+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v6.4.22",
@ -4540,6 +5077,75 @@
],
"time": "2025-04-26T23:47:35+00:00"
},
{
"name": "symfony/mailgun-mailer",
"version": "v7.1.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/mailgun-mailer.git",
"reference": "b0117bf42b6dd8dfcfcab2a7e18508b594520b5a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/b0117bf42b6dd8dfcfcab2a7e18508b594520b5a",
"reference": "b0117bf42b6dd8dfcfcab2a7e18508b594520b5a",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/mailer": "^6.4|^7.0"
},
"conflict": {
"symfony/http-foundation": "<6.4"
},
"require-dev": {
"symfony/http-client": "^6.4|^7.0",
"symfony/webhook": "^6.4|^7.0"
},
"type": "symfony-mailer-bridge",
"autoload": {
"psr-4": {
"Symfony\\Component\\Mailer\\Bridge\\Mailgun\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Mailgun Mailer Bridge",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/mailgun-mailer/tree/v7.1.6"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/mime",
"version": "v6.4.21",

View File

@ -168,6 +168,7 @@
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
Barryvdh\DomPDF\ServiceProvider::class,
])->toArray(),
/*
@ -182,7 +183,8 @@
*/
'aliases' => Facade::defaultAliases()->merge([
'PDF' => Barryvdh\DomPDF\Facade\Pdf::class,
// 'PDF' => Barryvdh\DomPDF\Facade\Pdf::class,
'PDF' => Barryvdh\DomPDF\Facade::class,
// 'Example' => App\Facades\Example::class,
])->toArray(),

View File

@ -37,8 +37,8 @@
'smtp' => [
'transport' => 'smtp',
'url' => env('MAIL_URL'),
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
'port' => env('MAIL_PORT', 587),
'host' => env('MAIL_HOST', 'smtp.mailtrap.io'),
'port' => env('MAIL_PORT', 2525),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),

View File

@ -14,11 +14,11 @@ class BarangPendukungSeeder extends Seeder
public function run(): void
{
BarangPendukung::insert([
['barang_id' => 1, 'kode' => 'PDN001'],
['barang_id' => 2, 'kode' => 'PDN002'],
['barang_id' => 3, 'kode' => 'PDN003'],
['barang_id' => 4, 'kode' => 'PDN004'],
['barang_id' => 5, 'kode' => 'PDN005'],
['barang_id' => 6, 'kode' => 'PDN001'],
['barang_id' => 7, 'kode' => 'PDN002'],
['barang_id' => 8, 'kode' => 'PDN003'],
['barang_id' => 9, 'kode' => 'PDN004'],
['barang_id' => 10, 'kode' => 'PDN005'],
]);
}
}

View File

@ -15,9 +15,14 @@ public function run(): void
// fungsi presensi dan penggajian
// KaryawanSeeder::class,
KloterSeeder::class,
TonIkanSeeder::class,
PresensiSeeder::class,
// KloterSeeder::class,
// TonIkanSeeder::class,
// PresensiSeeder::class,
// PresensiSeeder::class,
// PresensiSeeder::class,
// PresensiSeeder::class,
// PresensiSeeder::class,
// PresensiSeeder::class,
// Gudang Barang
// BarangSeeder::class,
@ -50,6 +55,24 @@ public function run(): void
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
// TransaksiSeeder::class,
TransaksiSeeder::class,
]);
}
}

View File

@ -15,6 +15,21 @@ class KloterSeeder extends Seeder
public function run(): void
{
DB::table('kloters')->insert([
[
'nama_kloter' => 'Kloter-1',
'created_at' => now(),
'updated_at' => now(),
],
[
'nama_kloter' => 'Kloter-2',
'created_at' => now(),
'updated_at' => now(),
],
[
'nama_kloter' => 'Kloter-3',
'created_at' => now(),
'updated_at' => now(),
],
[
'nama_kloter' => 'Kloter-4',
'created_at' => now(),

View File

@ -14,7 +14,14 @@ public function run(): void
{
$tonIkanData = [
[
'kloter_id' => 3,
'kloter_id' => 1,
'jumlah_ton' => 200.00,
'harga_ikan_per_ton' => 1000000,
'created_at' => now(),
'updated_at' => now(),
],
[
'kloter_id' => 2,
'jumlah_ton' => 200.00,
'harga_ikan_per_ton' => 1000000,
'created_at' => now(),

View File

@ -25,11 +25,11 @@ public function run(): void
$startDate = Carbon::now()->subYear();
$endDate = Carbon::now();
for ($i = 0; $i < 12; $i++) { // pilih bulan dari 1 - 12
$tipe = $i % 2 === 0 ? 'pemasukan' : 'pengeluaran'; // selang-seling
$tanggal = Carbon::create(2024, 1, 1)
for ($i = 0; $i < 5; $i++) { // pilih bulan dari 1 - 12
$tipe = 'pemasukan'; // selang-seling
$tanggal = Carbon::create(2025, 1, 1)
->addMonths($i)
->day(rand(1, 28)) // Acak tanggal antara 1 sampai 28 agar aman untuk semua bulan
->day(rand(26, 30)) // Acak tanggal antara 1 sampai 28 agar aman untuk semua bulan
->setTime(rand(0, 23), rand(0, 59), rand(0, 59)); // Acak waktu dalam hari
Transaksi::create([
@ -37,6 +37,7 @@ public function run(): void
'supplier_id' => $supplier->random()->id,
'pemasukan_id' => $tipe === 'pemasukan' ? $pemasukan->random()->id : null,
'pengeluaran_id' => $tipe === 'pengeluaran' ? $pengeluaran->random()->id : null,
'qty' => rand(1, 10),
'jumlahRp' => rand(300000, 900000),
'waktu_transaksi' => $tanggal,
]);

View File

@ -82,13 +82,13 @@ .sidebar-category {
}
.sidebar-submenu {
margin-left: 10px;
/* margin-left: 10px; */
font-size: 14px;
}
.sidebar-submenu .sidebar-link {
display: block;
padding: 4px 0 4px 20px;
padding: 4px 0 4px 0px;
color: #ccc;
}

View File

@ -127,4 +127,51 @@ document.querySelectorAll('.formPresensi').forEach(form => {
});
});
document.addEventListener('DOMContentLoaded', function () {
function handleJamEdit(selector, route, jamKey, errorMsg) {
document.querySelectorAll(selector).forEach(input => {
input.addEventListener('change', function () {
const id = this.dataset.id;
const jam = this.value;
const savedMsg = this.nextElementSibling;
fetch(route, {
method: "POST",
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': window.presensiConfig.csrfToken
},
body: JSON.stringify({
id: id,
[jamKey]: jam
})
})
.then(response => response.json())
.then(() => {
if (savedMsg) {
savedMsg.classList.remove('d-none');
setTimeout(() => savedMsg.classList.add('d-none'), 1500);
}
})
.catch(error => {
alert(errorMsg);
console.error(error);
});
});
});
}
handleJamEdit(
'.input-edit-jam-masuk',
window.presensiConfig.routeUpdateJamMasuk,
'jam_masuk',
"Gagal menyimpan jam masuk."
);
handleJamEdit(
'.input-edit-jam-pulang',
window.presensiConfig.routeUpdateJamPulang,
'jam_pulang',
"Gagal menyimpan jam pulang."
);
});

View File

@ -9,85 +9,85 @@
</div>
<div class="dashboard-container" style="height: 100%;">
<div class="info-row">
<div class="info-box text-start">
<div class="fw-bold">Pendapatan</div>
<div class="text-success fw-bold mt-1">Rp {{ number_format($keuangan['pendapatan'], 0, ',', '.') }}</div>
</div>
<div class="info-box">
<div>Pengeluaran</div>
<div class="text-danger mt-2">
Rp {{ number_format($keuangan['pengeluaran'], 0, ',', '.') }}
<div class="info-row">
<div class="info-box text-start">
<div class="fw-bold">Pendapatan</div>
<div class="text-success fw-bold mt-1">Rp {{ number_format($keuangan['pendapatan'], 0, ',', '.') }}</div>
</div>
<div class="info-box">
<div>Pengeluaran</div>
<div class="text-danger mt-2">
Rp {{ number_format($keuangan['pengeluaran'], 0, ',', '.') }}
</div>
</div>
</div>
</div>
<div class="chart-card bg-yellow-50 p-4 rounded shadow-md mt-4" style="height:auto;">
<div class="chart-c flex justify-between items-center mb-4">
<h2 class="font-bold text-lg">
Grafik Pendapatan <span class="text-dark">vs</span> Pengeluaran
<span class="text-sm text-gray-600">({{ $bulanAktif }})</span>
</h2>
<form id="date-filter-form" class="flex items-center gap-2" style="padding-top: 10px">
<input style="width: fit-content" type="date" name="start_date" id="start_date"
value="{{ request('start_date', now()->startOfMonth()->toDateString()) }}"
class="filter-info border px-2 py-1 rounded text-sm">
<input style="width: fit-content" type="date" name="end_date" id="end_date"
value="{{ request('end_date', now()->endOfMonth()->toDateString()) }}"
class="filter-info border px-2 py-1 rounded text-sm">
<!-- Tab Filter -->
<ul class="nav nav-tabs mt-4" id="chartTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="tanggal-tab" data-bs-toggle="tab" data-bs-target="#tanggalChartTab" type="button" role="tab">Grafik Tanggal</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="kloter-tab" data-bs-toggle="tab" data-bs-target="#kloterChartTab" type="button" role="tab">Grafik Kloter</button>
</li>
</ul>
<select id="kloterFilter" name="kloter_id" class="border px-2 py-1 rounded text-sm">
<option value="">Pilih Kloter</option>
@foreach($kloters as $kloter)
<option value="{{ $kloter->id }}"
data-start="{{ $kloter->tanggal_awal }}"
data-end="{{ $kloter->tanggal_akhir }}"
{{ request('kloter_id') == $kloter->id ? 'selected' : '' }}>
Kloter {{ $kloter->kloter_id }} ({{ $kloter->tanggal_awal }} - {{ $kloter->tanggal_akhir }})
</option>
@endforeach
</select>
</form>
</div>
<div class="chart-card">
<canvas id="financeChart"></canvas>
<div class="tab-content" id="chartTabContent">
<!-- Grafik Tanggal -->
<div class="tab-pane fade show active" id="tanggalChartTab" role="tabpanel">
<div class="chart-card bg-yellow-50 p-4 rounded shadow-md mt-4" style="height:auto;">
<div class="chart-c flex justify-between items-center mb-4">
<h2 class="font-bold text-lg">
Grafik Pendapatan <span class="text-dark">vs</span> Pengeluaran
<span class="text-sm text-gray-600">({{ $bulanAktif }})</span>
</h2>
<form id="date-filter-form" class="flex items-center gap-2" style="padding-top: 10px">
<input style="width: fit-content" type="date" name="start_date" id="start_date"
value="{{ request('start_date', now()->startOfMonth()->toDateString()) }}"
class="filter-info border px-2 py-1 rounded text-sm">
<input style="width: fit-content" type="date" name="end_date" id="end_date"
value="{{ request('end_date', now()->endOfMonth()->toDateString()) }}"
class="filter-info border px-2 py-1 rounded text-sm">
</form>
</div>
<div class="chart-card">
<canvas id="financeChart"></canvas>
</div>
</div>
</div>
<!-- Grafik Kloter -->
<div class="tab-pane fade" id="kloterChartTab" role="tabpanel">
<div class="chart-card bg-yellow-50 p-4 rounded shadow-md mt-4" style="height:auto;">
<div class="chart-c flex justify-between items-center mb-4">
<h2 class="font-bold text-lg">
Grafik Pendapatan <span class="text-dark">vs</span> Pengeluaran per Kloter
</h2>
<form id="kloter-filter-form" class="flex items-center gap-2" style="padding-top: 10px">
<select id="kloterFilter" name="kloter_id" class="border px-2 py-1 rounded text-sm">
<option value="">Pilih Kloter</option>
@foreach($kloters as $kloter)
<option value="{{ $kloter->id }}"
data-start="{{ $kloter->tanggal_awal }}"
data-end="{{ $kloter->tanggal_akhir }}"
{{ request('kloter_id') == $kloter->id ? 'selected' : '' }}>
Kloter {{ $kloter->kloter_id }} ({{ $kloter->tanggal_awal }} - {{ $kloter->tanggal_akhir }})
</option>
@endforeach
</select>
</form>
</div>
<div class="chart-card">
<canvas id="kloterChart"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const form = document.getElementById('date-filter-form');
const startInput = document.getElementById('start_date');
const endInput = document.getElementById('end_date');
[startInput, endInput].forEach(input => {
input.addEventListener('change', () => {
const startDate = startInput.value;
const endDate = endInput.value;
if (startDate && endDate && startDate <= endDate) {
form.submit();
}
});
});
document.getElementById('kloterFilter').addEventListener('change', function () {
const selected = this.options[this.selectedIndex];
const start = selected.getAttribute('data-start');
const end = selected.getAttribute('data-end');
if (start && end) {
document.getElementById('start_date').value = start;
document.getElementById('end_date').value = end;
}
document.getElementById('date-filter-form').submit();
});
// Grafik Tanggal
const ctx = document.getElementById('financeChart').getContext('2d');
const financeChart = new Chart(ctx, {
type: 'line',
@ -100,52 +100,127 @@ class="filter-info border px-2 py-1 rounded text-sm">
borderColor: 'blue',
backgroundColor: 'blue',
fill: false,
tension: 0.4, // Membuat garis lengkung
tension: 0.4,
pointRadius: 4,
pointBackgroundColor: 'blue',
borderWidth: 3, // Garis lebih tebal
showLine: true, // Pastikan garis antar titik muncul
},
{
label: 'Pengeluaran',
data: [0, ...{!! json_encode($pengeluaranBulanan) !!}, 0], // Awali & akhiri dengan 0
borderColor: 'red',
backgroundColor: 'red',
fill: false,
tension: 0.4, // Membuat garis lengkung
pointRadius: 4,
pointBackgroundColor: 'red',
borderWidth: 4, // Garis lebih tebal
showLine: true // Pastikan garis antar titik muncul
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
elements: {
line: {
cubicInterpolationMode: 'monotone', // Garis lengkung halus
}
borderWidth: 3,
showLine: true,
},
spanGaps: true, // Hubungkan titik meskipun ada data kosong
scales: {
y: {
type: 'logarithmic',
beginAtZero: true,
ticks: {
callback: function(value, index, ticks) {
return 'Rp ' + value.toLocaleString('id-ID');
}
{
label: 'Pengeluaran',
data: {!! json_encode($pengeluaranBulanan) !!},
borderColor: 'red',
backgroundColor: 'red',
fill: false,
tension: 0.4,
pointRadius: 4,
pointBackgroundColor: 'red',
borderWidth: 4,
showLine: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
elements: {
line: {
cubicInterpolationMode: 'monotone',
}
},
spanGaps: true,
scales: {
y: {
type: 'logarithmic',
beginAtZero: true,
ticks: {
callback: function(value) {
return 'Rp ' + value.toLocaleString('id-ID');
}
}
}
}
});
}
});
// Tambahkan titik 0 di awal dan akhir dataset untuk Pendapatan
financeChart.data.datasets[0].data = [0, ...{!! json_encode($pendapatanBulanan) !!}, 0];
financeChart.data.labels = ['0', ...{!! json_encode($labels) !!}, ' X'];
financeChart.update();
// Grafik Kloter
const ctxKloter = document.getElementById('kloterChart').getContext('2d');
const kloterChart = new Chart(ctxKloter, {
type: 'line',
data: {
labels: {!! json_encode(array_reverse($labelsKloter)) !!}, // diurut terbalik
datasets: [
{
label: 'Pendapatan',
data: {!! json_encode(array_reverse($pendapatanKloter)) !!},
borderColor: 'blue',
backgroundColor: 'blue',
fill: false,
tension: 0.4,
pointRadius: 4,
pointBackgroundColor: 'blue',
borderWidth: 3,
showLine: true,
},
{
label: 'Pengeluaran',
data: {!! json_encode(array_reverse($pengeluaranKloter)) !!},
borderColor: 'red',
backgroundColor: 'red',
fill: false,
tension: 0.4,
pointRadius: 4,
pointBackgroundColor: 'red',
borderWidth: 4,
showLine: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
elements: {
line: {
cubicInterpolationMode: 'monotone',
}
},
spanGaps: true,
scales: {
y: {
// type: 'logarithmic',
beginAtZero: true,
ticks: {
callback: function(value) {
return 'Rp ' + value.toLocaleString('id-ID');
}
}
}
}
}
});
// Filter tanggal
const form = document.getElementById('date-filter-form');
const startInput = document.getElementById('start_date');
const endInput = document.getElementById('end_date');
[startInput, endInput].forEach(input => {
input.addEventListener('change', () => {
const startDate = startInput.value;
const endDate = endInput.value;
if (startDate && endDate && startDate <= endDate) {
form.submit();
}
});
});
document.getElementById('kloterFilter').addEventListener('change', function () {
const selected = this.options[this.selectedIndex];
const start = selected.getAttribute('data-start');
const end = selected.getAttribute('data-end');
if (start && end) {
document.getElementById('start_date').value = start;
document.getElementById('end_date').value = end;
}
form.submit();
});
</script>
@endsection

View File

@ -0,0 +1,2 @@
<h1>Halo!</h1>
<p>Email ini berhasil dikirim dari Laravel menggunakan Mailtrap.</p>

View File

@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lupa Password - UD.DNL PUTRA</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-blue-200 flex items-center justify-center h-screen">
<div class="bg-slate-400 p-6 rounded shadow-md w-full max-w-sm">
<h2 class="text-center text-white font-bold mb-4 bg-slate-600 py-2 rounded">RESET PASSWORD<br>UD.DNL PUTRA</h2>
@if (session('status'))
<div class="bg-green-100 text-green-800 p-2 rounded mb-3 text-sm">
{{ session('status') }}
</div>
@endif
@if ($errors->any())
<div class="bg-red-100 text-red-800 p-2 rounded mb-3 text-sm">
{{ $errors->first() }}
</div>
@endif
<form method="POST" action="{{ route('password.email') }}">
@csrf
<input type="email" name="email" placeholder="Email" class="w-full px-3 py-2 rounded mb-3 bg-slate-500 text-white placeholder-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500" required autofocus>
<button type="submit" class="w-full bg-slate-700 text-white py-2 rounded hover:bg-slate-800">
Kirim Link Reset
</button>
</form>
<div class="mt-3 text-center text-sm">
<a href="{{ route('login') }}" class="text-blue-100 hover:underline">Kembali ke login</a>
</div>
</div>
</body>
</html>

View File

@ -23,7 +23,7 @@
<!-- Kategori: Data -->
<div class="sidebar-category">Data</div>
<a href="/operator/barang" class="sidebar-link">Barang</a>
<div class="sidebar-submenu ms-3">
<div class="sidebar-submenu">
<a href="{{ route('barang.index', ['filter' => 'produk']) }}" class="sidebar-link">Produk</a>
<a href="{{ route('barang.index', ['filter' => 'pendukung']) }}" class="sidebar-link">Pendukung</a>
</div>

View File

@ -37,6 +37,11 @@ class="w-full px-4 py-2 bg-gray-600 text-white rounded focus:outline-none"
class="w-full px-4 py-2 bg-gray-600 text-white rounded focus:outline-none"
placeholder="Password"
required>
<div class="text-right mt-1">Lupa Password?
<a href="{{ route('password.request') }}" class="text-white underline hover:text-gray-200 text-sm">
Reset
</a>
</div>
</div>
<div>

View File

@ -43,7 +43,7 @@ class="form-control mb-3"
<th>Nama Pekerja</th>
<th>Jenis Kelamin</th>
@foreach ($tanggalUnik as $tanggal)
<th>{{ \Carbon\Carbon::parse($tanggal)->format('d-M-y') }}</th>
<th>{{ \Carbon\Carbon::parse($tanggal)->format('d-M-Y') }}</th>
@endforeach
<th>Total Jam Kerja</th>
<th>Gaji Per Jam</th>
@ -52,13 +52,24 @@ class="form-control mb-3"
</thead>
<tbody>
@foreach ($karyawanWithGaji as $data)
<tr>
<tr style="text-align: -webkit-center;">
<td>{{ $data['karyawan']->nama }}</td>
<td>{{ $data['karyawan']->jenis_kelamin }}</td>
@foreach ($tanggalUnik as $tanggal)
<td>{{ number_format($data['jam_per_tanggal'][$tanggal], 2) }}</td>
@php
$jamFloat = $data['jam_per_tanggal'][$tanggal] ?? 0;
$totalMenit = round($jamFloat * 60);
$jam = floor($totalMenit / 60);
$menit = $totalMenit % 60;
@endphp
<td>{{ $jam }} jam {{ $menit }} menit</td>
@endforeach
<td>{{ number_format($data['total_jam'], 2) }}</td>
@php
$totalMenit = round($data['total_jam'] * 60);
$totalJam = floor($totalMenit / 60);
$totalMenit = $totalMenit % 60;
@endphp
<td>{{ $totalJam }} jam {{ $totalMenit }} menit</td>
<td>{{ number_format($data['gaji_per_jam'], 0, ',', '.') }}</td>
<td>{{ number_format($data['total_gaji'], 0, ',', '.') }}</td>
</tr>

View File

@ -74,7 +74,7 @@
$totalJam = \Carbon\Carbon::parse($p->jam_masuk)->diffInMinutes(\Carbon\Carbon::parse($p->jam_pulang)) / 60;
}
@endphp
<tr>
<tr style="text-align: -webkit-center;">
<td>{{ $i + 1 }}</td>
<td>{{ $k->nama }}</td>
{{-- Aksi Masuk --}}
@ -94,7 +94,20 @@
</td>
{{-- Jam Masuk --}}
<td>{{ $p->jam_masuk ?? '-' }}</td>
<td>
@if($p && $p->jam_masuk)
<input
type="time"
value="{{ \Carbon\Carbon::parse($p->jam_masuk)->format('H:i') }}"
class="form-control form-control-sm input-edit-jam-masuk"
data-id="{{ $p->id }}"
style="width: 110px;"
>
<small class="text-success d-none saved-message"></small>
@else
-
@endif
</td>
{{-- Aksi Pulang --}}
<td>
@ -115,7 +128,21 @@
</td>
{{-- Jam Pulang --}}
<td>{{ $p->jam_pulang ?? '-' }}</td>
{{-- <td>{{ $p->jam_pulang ?? '-' }}</td> --}}
<td>
@if($p && $p->jam_pulang)
<input
type="time"
value="{{ \Carbon\Carbon::parse($p->jam_pulang)->format('H:i') }}"
class="form-control form-control-sm input-edit-jam-pulang"
data-id="{{ $p->id }}"
style="width: 110px;"
>
<small class="text-success d-none saved-message"></small>
@else
-
@endif
</td>
{{-- Total Jam --}}
<td>{{ number_format($totalJam, 2) }}</td>
@ -148,5 +175,13 @@
@endif
</script>
<script>
window.presensiConfig = {
routeUpdateJamMasuk: "{{ route('presensi.updateJamMasukAjax') }}",
routeUpdateJamPulang: "{{ route('presensi.updateJamPulangAjax') }}",
csrfToken: "{{ csrf_token() }}"
};
</script>
<script src="{{ asset('js/presensi.js') }}"></script>
@endsection

View File

@ -20,6 +20,10 @@
<input type="hidden" name="tanggal_akhir" value="{{ request('tanggal_akhir') }}">
<input type="hidden" name="q" id="exportSearchQuery">
<button type="submit" class="btn btn-success btn-sm mb-3">Export Excel</button>
<button type="button" class="btn btn-danger btn-sm mb-3" onclick="window.open('{{ route('operator.transaksi.export_pdf', ['tanggal_mulai' => request('tanggal_mulai'), 'tanggal_akhir' => request('tanggal_akhir'), 'q' => request('q')]) }}', '_blank')">
Export PDF
</button>
</form>
</div>
@ -43,7 +47,10 @@
<tbody>
@foreach($data as $i => $trx)
<tr>
{{-- gunakan kode dibawah jika ingin urutan nomer dimuali dari 1 --}}
<td class="p-2">{{ $i + 1 }}</td>
{{-- gunakan kode dibasah jika ingin urutan nomer terbalik --}}
{{-- <td class="p-2">{{ count($data) - $i }}</td> --}}
<td class="p-2">{{ \Carbon\Carbon::parse($trx['waktu_transaksi'])->format('d-m-Y H:i') }}</td>
<td class="p-2">{{ $trx['kode_transaksi'] }}</td>
<td class="p-2">{{ $trx['kode_barang'] }}</td>
@ -57,7 +64,7 @@
<!-- Tombol Edit -->
<button
type="button"
class="btn btn-primary btn-edit"
class="btn btn-primary btn-edit btn-sm"
data-bs-toggle="modal"
data-bs-target="#editModal"
data-id="{{ $trx['id'] }}"

View File

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Laporan Transaksi</title>
<style>
body {
font-family: sans-serif;
font-size: 12px;
color: #000;
}
h2 {
text-align: center;
margin-bottom: 10px;
}
.info {
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
}
th, td {
border: 1px solid #444;
padding: 6px;
text-align: center;
}
.ttd {
width: 100%;
margin-top: 60px;
text-align: right;
}
.ttd p {
margin-bottom: 70px;
}
</style>
</head>
<body>
<h3 style="text-align: center;">Laporan Transaksi Keuangan</h3>
<p>Periode: {{ $tanggalMulai ?? '-' }} s/d {{ $tanggalAkhir ?? '-' }}</p>
<p>Filter Kata Kunci: {{ $q ?? '-' }}</p>
<table>
<thead>
<tr>
<th>No</th>
<th>Waktu</th>
<th>Kode Transaksi</th>
<th>Kode Barang</th>
<th>Nama Mitra</th>
<th>Nama Barang</th>
<th>Qty</th>
<th>Masuk</th>
<th>Keluar</th>
<th>Total</th>
</tr>
</thead>
<tbody>
@foreach($data as $i => $trx)
<tr>
<td>{{ $i + 1 }}</td>
<td>{{ \Carbon\Carbon::parse($trx['Waktu'])->format('d-m-Y H:i') }}</td>
<td>{{ $trx['Kode Transaksi'] }}</td>
<td>{{ $trx['Kode Barang'] }}</td>
<td>{{ $trx['Supplier'] }}</td>
<td>{{ $trx['Nama Barang'] }}</td>
<td>{{ $trx['Qty'] }}</td>
<td>Rp {{ number_format($trx['Masuk'], 0, ',', '.') }}</td>
<td>Rp {{ number_format($trx['Keluar'], 0, ',', '.') }}</td>
<td>Rp {{ number_format($trx['Total'], 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
</table>
<br><br>
<div style="text-align: right;">
<p>Mengetahui,</p>
<p style="margin-top: 60px;">Poniman</p>
<p>....................................</p>
</div>
</body>
</html>

View File

@ -6,19 +6,21 @@
<div class="container mt-4">
<h3>Laporan Barang</h3>
<div class="row mb-3">
<div class="col-md-3">
<select id="filter" class="form-select">
<option value="">-- Semua Kategori --</option>
<option value="produk" {{ request('filter') == 'produk' ? 'selected' : '' }}>Produk</option>
<option value="pendukung" {{ request('filter') == 'pendukung' ? 'selected' : '' }}>Pendukung</option>
</select>
<div style="width: auto; display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: center;">
<div class="col-md-3 d-flex gap-0">
<button class="btn btn-outline-primary filter-btn {{ request('filter') == 'produk' ? 'active' : '' }}" data-filter="produk">Produk</button>
<button class="btn btn-outline-primary filter-btn {{ request('filter') == 'pendukung' ? 'active' : '' }}" data-filter="pendukung">Pendukung</button>
<input type="hidden" id="filter" value="{{ request('filter') }}">
</div>
<div class="col-md-4">
<input type="text" id="nama" class="form-control" placeholder="Cari nama barang..." value="{{ request('nama') }}">
</div>
<div class="col-md-2">
<button id="downloadExcel" class="btn btn-success w-100">Unduh Excel</button>
<div class="col d-flex gap-2 mb-2" style="flex-wrap: nowrap; flex-direction: row; justify-content: flex-end; align-items: center;">
<div class="col-md-4" style="width: auto">
<input type="text" id="nama" class="form-control" placeholder="Cari nama barang..." value="{{ request('nama') }}">
</div>
<div class="col-md-2 d-flex gap-0" style="width: fit-content;">
<button id="downloadExcel" class="btn btn-success">Unduh Excel</button>
<button id="downloadPDF" class="btn btn-danger">Unduh PDF</button>
</div>
</div>
</div>
@ -29,8 +31,10 @@
<script>
document.addEventListener('DOMContentLoaded', function () {
const filterSelect = document.getElementById('filter');
const filterButtons = document.querySelectorAll('.filter-btn');
const namaInput = document.getElementById('nama');
const downloadBtn = document.getElementById('downloadExcel');
const downloadBtnPDF = document.getElementById('downloadPDF');
const container = document.getElementById('dataContainer');
async function fetchData() {
@ -54,7 +58,24 @@
}
}
filterSelect.addEventListener('change', fetchData);
filterButtons.forEach(btn => {
btn.addEventListener('click', function () {
const current = document.getElementById('filter').value;
const selected = this.dataset.filter;
if (current === selected) {
// Nonaktifkan jika tombol aktif ditekan lagi
document.getElementById('filter').value = '';
filterButtons.forEach(b => b.classList.remove('active'));
} else {
document.getElementById('filter').value = selected;
filterButtons.forEach(b => b.classList.remove('active'));
this.classList.add('active');
}
fetchData();
});
});
let timeout = null;
namaInput.addEventListener('input', function () {
@ -71,6 +92,15 @@
});
window.location.href = "{{ route('laporan.barang') }}" + '?' + urlParams.toString();
});
downloadBtnPDF.addEventListener('click', function (e) {
e.preventDefault();
const urlParams = new URLSearchParams({
filter: filterSelect.value,
nama: namaInput.value,
export: 'pdf'
});
window.location.href = "{{ route('laporan.barang') }}" + '?' + urlParams.toString();
});
});
</script>

View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Laporan Transaksi</title>
<style>
body {
font-family: sans-serif;
font-size: 12px;
color: #000;
}
h2 {
text-align: center;
margin-bottom: 10px;
}
.info {
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
}
th, td {
border: 1px solid #444;
padding: 6px;
text-align: center;
}
.ttd {
width: 100%;
margin-top: 60px;
text-align: right;
}
.ttd p {
margin-bottom: 70px;
}
</style>
</head>
<body>
<h3 style="text-align: center;">Laporan Barang</h3>
<p>Filter: {{ $filter ?? '-' }}</p>
<p>Nama Barang: {{ $nama ?? '-' }}</p>
<table>
<thead>
<tr>
<th>No</th>
<th>Kode</th>
<th>Nama Barang</th>
<th>Exp</th>
<th>Harga</th>
<th>Qty</th>
</tr>
</thead>
<tbody>
@foreach($barangs as $i => $b)
<tr>
<td>{{ $i + 1 }}</td>
<td>{{ $b->produk->kode ?? $b->pendukung->kode ?? '-' }}</td>
<td>{{ $b->nama_barang }}</td>
<td>{{ optional($b->exp)->format('d-m-Y') }}</td>
<td>Rp {{ number_format($b->harga, 0, ',', '.') }}</td>
<td>{{ $b->qty }}</td>
</tr>
@endforeach
</tbody>
</table>
<br><br>
<div style="text-align: right;">
<p>Mengetahui,</p>
<p style="margin-top: 60px;">Poniman</p>
<p>....................................</p>
</div>
</body>
</html>

View File

@ -9,24 +9,26 @@
<form id="filterForm" class="mb-4">
{{-- @csrf --}}
<div class="row g-2 align-items-end">
<div class="col-auto">
<label for="filter" class="form-label">Filter Tanggal:</label>
<select name="filter" id="filter" class="form-select">
<option value="minggu_ini">Minggu Ini</option>
<option value="hari_ini">Hari Ini</option>
<option value="bulan_ini">Bulan Ini</option>
<option value="kloter_terbaru">Kloter Terbaru</option>
<option value="semua">Semua</option>
</select>
</div>
<div class="col-auto">
<label for="nama" class="form-label">Cari Nama:</label>
<input type="text" name="nama" id="nama" class="form-control" placeholder="Ketik nama...">
<div class="col d-flex gap-2">
<div class="col-auto">
<label for="nama" class="form-label">Cari Nama:</label>
<input type="text" name="nama" id="nama" class="form-control" placeholder="Ketik nama...">
</div>
<div class="col-auto">
<label for="filter" class="form-label">Filter Tanggal:</label>
<select name="filter" id="filter" class="form-select">
<option value="minggu_ini">Minggu Ini</option>
<option value="hari_ini">Hari Ini</option>
<option value="bulan_ini">Bulan Ini</option>
<option value="kloter_terbaru">Kloter Terbaru</option>
<option value="semua">Semua</option>
</select>
</div>
</div>
<div class="col-auto mt-4">
<a id="downloadExcel" class="btn btn-success">Unduh Excel</a>
<a id="downloadPDF" class="btn btn-danger">Unduh PDF</a>
</div>
</div>
</form>
@ -40,6 +42,7 @@
const filterSelect = document.getElementById('filter');
const namaInput = document.getElementById('nama');
const downloadBtn = document.getElementById('downloadExcel');
const downloadBtnPDF = document.getElementById('downloadPDF');
const container = document.getElementById('dataContainer');
async function fetchData() {
@ -84,6 +87,15 @@
});
window.location.href = "{{ route('laporan.karyawan') }}" + '?' + urlParams.toString();
});
downloadBtnPDF.addEventListener('click', function (e) {
e.preventDefault();
const urlParams = new URLSearchParams({
filter: filterSelect.value,
nama: namaInput.value,
export: 'pdf'
});
window.location.href = "{{ route('laporan.karyawan') }}" + '?' + urlParams.toString();
});
});
</script>
@endsection

View File

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Laporan Transaksi</title>
<style>
body {
font-family: sans-serif;
font-size: 12px;
color: #000;
}
h2 {
text-align: center;
margin-bottom: 10px;
}
.info {
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
}
th, td {
border: 1px solid #444;
padding: 6px;
text-align: center;
}
.ttd {
width: 100%;
margin-top: 60px;
text-align: right;
}
.ttd p {
margin-bottom: 70px;
}
</style>
</head>
<body>
<h3 style="text-align: center;">Laporan Gaji Karyawan</h3>
<p>Filter Waktu: {{ $filter ?? '-' }}</p>
<p>Nama: {{ $nama ?? '-' }}</p>
<table>
<thead>
<tr>
<th>No</th>
<th>Nama Pekerja</th>
<th>Jenis Kelamin</th>
<th>No Telepon</th>
<th>Total Jam Kerja</th>
<th>Gaji per Kloter</th>
<th>Total Gaji</th>
</tr>
</thead>
<tbody>
@foreach($data as $i => $item)
<tr>
<td>{{ $i + 1 }}</td>
<td>{{ $item['karyawan']->nama }}</td>
<td>{{ $item['karyawan']->jenis_kelamin === 'L' ? 'Laki-laki' : 'Perempuan' }}</td>
<td>{{ $item['karyawan']->no_telepon }}</td>
<td>{{ $item['total_jam_kerja'] }} Jam</td>
<td>
@foreach($item['gaji_per_kloter'] as $gaji)
Kloter ID {{ $gaji['kloter_id'] }}: Rp {{ number_format($gaji['gaji'], 0, ',', '.') }} ({{ $gaji['total_jam'] }} jam)<br>
@endforeach
</td>
<td>Rp {{ number_format($item['total_gaji'], 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
</table>
<br><br>
<div style="text-align: right;">
<p>Mengetahui,</p>
<p style="margin-top: 60px;">Poniman</p>
<p>....................................</p>
</div>
</body>
</html>

View File

@ -7,19 +7,20 @@
<h4>Laporan Mitra Bisnis</h4>
<!-- Filter -->
<div class="row mb-3">
<div class="col-md-4">
<input type="text" id="keyword" class="form-control" placeholder="Cari Nama atau Alamat Mitra">
<div class="mb-3" style="width: auto; display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: center;">
<div class="col-md-4 d-flex gap-0">
<button class="btn btn-outline-primary kategori-btn" data-kategori="pemasok">Pemasok</button>
<button class="btn btn-outline-primary kategori-btn" data-kategori="konsumen">Konsumen</button>
<input type="hidden" id="kategori" value="">
</div>
<div class="col-md-4">
<select id="kategori" class="form-select">
<option value="">Semua Kategori</option>
<option value="pemasok">Pemasok</option>
<option value="konsumen">Konsumen</option>
</select>
</div>
<div class="col-md-4">
<button id="unduhExcel" class="btn btn-success">Unduh Excel</button>
<div class="col d-flex gap-2 mb-2" style="flex-wrap: nowrap; flex-direction: row; justify-content: flex-end; align-items: center;">
<div class="col-md-4">
<input type="text" id="keyword" class="form-control" placeholder="Cari Nama atau Alamat Mitra">
</div>
<div class="col-md-2 d-flex gap-0" style="width: fit-content;">
<button id="unduhExcel" class="btn btn-success">Unduh Excel</button>
<button id="unduhPDF" class="btn btn-danger">Unduh PDF</button>
</div>
</div>
</div>
@ -31,49 +32,77 @@
<script>
document.addEventListener('DOMContentLoaded', function () {
const keywordInput = document.getElementById('keyword');
const kategoriSelect = document.getElementById('kategori');
const unduhBtn = document.getElementById('unduhExcel');
const container = document.getElementById('dataContainer');
const keywordInput = document.getElementById('keyword');
const kategoriButtons = document.querySelectorAll('.kategori-btn');
const kategoriInput = document.getElementById('kategori');
const unduhBtn = document.getElementById('unduhExcel');
const unduhBtnPDF = document.getElementById('unduhPDF');
const container = document.getElementById('dataContainer');
async function fetchData() {
const params = new URLSearchParams({
keyword: keywordInput.value,
kategori: kategoriSelect.value,
ajax: 1
});
try {
const response = await fetch("{{ route('laporan.supplier') }}?" + params.toString(), {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
async function fetchData() {
const params = new URLSearchParams({
keyword: keywordInput.value,
kategori: kategoriInput.value,
ajax: 1
});
if (!response.ok) throw new Error('Gagal memuat data');
try {
const response = await fetch("{{ route('laporan.supplier') }}?" + params.toString(), {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
});
const html = await response.text();
container.innerHTML = html;
} catch (err) {
alert('Error: ' + err.message);
if (!response.ok) throw new Error('Gagal memuat data');
const html = await response.text();
container.innerHTML = html;
} catch (err) {
alert('Error: ' + err.message);
}
}
}
keywordInput.addEventListener('input', () => {
clearTimeout(window.keywordTimeout);
window.keywordTimeout = setTimeout(fetchData, 500);
});
kategoriButtons.forEach(btn => {
btn.addEventListener('click', function () {
const currentValue = kategoriInput.value;
const newValue = this.dataset.kategori;
kategoriSelect.addEventListener('change', fetchData);
if (currentValue === newValue) {
kategoriInput.value = '';
kategoriButtons.forEach(b => b.classList.remove('active'));
} else {
kategoriInput.value = newValue;
kategoriButtons.forEach(b => b.classList.remove('active'));
this.classList.add('active');
}
unduhBtn.addEventListener('click', function () {
const params = new URLSearchParams({
keyword: keywordInput.value,
kategori: kategoriSelect.value,
export: 'excel'
fetchData();
});
});
window.location.href = "{{ route('laporan.supplier') }}" + '?' + params.toString();
keywordInput.addEventListener('input', () => {
clearTimeout(window.keywordTimeout);
window.keywordTimeout = setTimeout(fetchData, 500);
});
unduhBtn.addEventListener('click', function () {
const params = new URLSearchParams({
keyword: keywordInput.value,
kategori: kategoriInput.value,
export: 'excel'
});
window.location.href = "{{ route('laporan.supplier') }}" + '?' + params.toString();
});
unduhBtnPDF.addEventListener('click', function () {
const params = new URLSearchParams({
keyword: keywordInput.value,
kategori: kategoriInput.value,
export: 'pdf'
});
window.location.href = "{{ route('laporan.supplier') }}" + '?' + params.toString();
});
});
});
</script>
@endsection

View File

@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Laporan Transaksi</title>
<style>
body {
font-family: sans-serif;
font-size: 12px;
color: #000;
}
h2 {
text-align: center;
margin-bottom: 10px;
}
.info {
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
}
th, td {
border: 1px solid #444;
padding: 6px;
text-align: center;
}
.ttd {
width: 100%;
margin-top: 60px;
text-align: right;
}
.ttd p {
margin-bottom: 70px;
}
</style>
</head>
<body>
<h3 style="text-align: center;">Laporan Data Mitra Bisnis</h3>
<p>Kategori: {{ $kategori ?? 'Semua' }}</p>
<p>Kata Kunci: {{ $keyword ?? '-' }}</p>
<table>
<thead>
<tr>
<th>No</th>
<th>Nama</th>
<th>Kategori</th>
<th>Alamat</th>
<th>No Telepon</th>
<th>No Rekening</th>
</tr>
</thead>
<tbody>
@foreach($suppliers as $i => $supplier)
<tr>
<td>{{ $i + 1 }}</td>
<td>{{ $supplier->nama }}</td>
<td>{{ $supplier->pemasok ? 'Pemasok' : 'Konsumen' }}</td>
<td>{{ $supplier->alamat }}</td>
<td>{{ $supplier->no_tlp }}</td>
<td>{{ $supplier->no_rekening ?? '-' }}</td>
</tr>
@endforeach
</tbody>
</table>
<br><br>
<div style="text-align: right;">
<p>Mengetahui,</p>
<p style="margin-top: 60px;">Poniman</p>
<p>....................................</p>
</div>
</body>
</html>

View File

@ -7,20 +7,18 @@
<h1 class="mb-4">Laporan Transaksi</h1>
<form id="filterForm" method="GET" class="mb-4 flex items-center gap-2" style="text-align-last: end">
<input type="text" name="q" id="q" placeholder="Cari kode, nama barang, atau Mitra..." value="{{ request('q') }}" class="border px-2 py-1 rounded" style="text-align-last: start;">
<div style="width: auto; display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: center;">
<input type="text" name="daterange" id="daterange" placeholder="Pilih rentang tanggal" value="{{ request('daterange') }}" class="border px-2 py-1 rounded">
<div class="col d-flex gap-2 mb-2" style="flex-wrap: nowrap; flex-direction: row; justify-content: flex-end; align-items: center;">
<input type="text" name="q" id="q" placeholder="Cari kode, nama barang, atau Mitra..." value="{{ request('q') }}" class="border px-2 py-1 rounded" style="text-align-last: start;">
<input type="text" name="daterange" id="daterange" placeholder="Pilih rentang tanggal" value="{{ request('daterange') }}" class="border px-2 py-1 rounded">
{{-- <input type="date" name="tanggal_mulai" id="tanggal_mulai" value="{{ request('tanggal_mulai') }}" class="border px-2 py-1 rounded">
<span>s/d</span>
<input type="date" name="tanggal_akhir" id="tanggal_akhir" value="{{ request('tanggal_akhir') }}" class="border px-2 py-1 rounded"> --}}
<button type="submit" name="export" value="excel" class="btn btn-success" id="downloadExcel">
Unduh Excel
</button>
<button type="submit" name="export" value="pdf" class="btn btn-danger" id="downloadPDF">
Unduh PDF
</button>
<div class="col-md-2 d-flex gap-0" style="width: fit-content;">
<button type="submit" name="export" value="excel" class="btn btn-success" id="downloadExcel">Unduh Excel</button>
<button type="submit" name="export" value="pdf" class="btn btn-danger" id="downloadPDF">Unduh PDF</button>
</div>
</div>
</div>
</form>
<div class="table-responsive" id="tabelTransaksi">

View File

@ -37,13 +37,9 @@
</style>
</head>
<body>
<h2>Laporan Transaksi Keuangan</h2>
<div class="info">
<p><strong>Periode:</strong> {{ $tanggal_mulai ?? '-' }} s/d {{ $tanggal_akhir ?? '-' }}</p>
<p><strong>Filter Kata Kunci:</strong> {{ $q ?? '-' }}</p>
</div>
<h3 style="text-align: center;">Laporan Transaksi Keuangan</h3>
<p>Periode: {{ $tanggalMulai ?? '-' }} s/d {{ $tanggalAkhir ?? '-' }}</p>
<p>Filter Kata Kunci: {{ $q ?? '-' }}</p>
<table>
<thead>
@ -61,28 +57,36 @@
</tr>
</thead>
<tbody>
@foreach ($data as $i => $row)
<tr>
<td>{{ $i + 1 }}</td>
<td>{{ \Carbon\Carbon::parse($row['waktu'])->format('d-m-Y H:i') }}</td>
<td>{{ $row['kode_transaksi'] }}</td>
<td>{{ $row['kode_barang'] }}</td>
<td>{{ $row['supplier'] }}</td>
<td>{{ $row['nama_barang'] }}</td>
<td>{{ $row['qty'] }}</td>
<td>Rp {{ number_format($row['masuk'], 0, ',', '.') }}</td>
<td>Rp {{ number_format($row['keluar'], 0, ',', '.') }}</td>
<td>Rp {{ number_format($row['total'], 0, ',', '.') }}</td>
</tr>
@foreach($data as $i => $trx)
<tr>
<td>{{ $i + 1 }}</td>
<td>{{ \Carbon\Carbon::parse($trx['waktu'])->format('d-m-Y H:i') }}</td>
<td>{{ $trx['kode_transaksi'] }}</td>
<td>{{ $trx['kode_barang'] }}</td>
<td>{{ $trx['supplier'] }}</td>
<td>{{ $trx['nama_barang'] }}</td>
<td>{{ $trx['qty'] }}</td>
<td>Rp {{ number_format($trx['masuk'], 0, ',', '.') }}</td>
<td>Rp {{ number_format($trx['keluar'], 0, ',', '.') }}</td>
<td>Rp {{ number_format($trx['total'], 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
</table>
@foreach($data as $i => $trx)
<p>
@if($loop->last)
Transaksi terakhir dilakukan pada tanggal {{ \Carbon\Carbon::parse($trx['waktu'])->format('d-m-Y H:i') }} sebanyak Rp {{ number_format($trx['total'], 0, ',', '.') }}.
@endif
</p>
@endforeach
<div class="ttd">
<br><br>
<div style="text-align: right;">
<p>Mengetahui,</p>
<p><strong>Poniman</strong></p>
<p style="text-decoration: underline;">....................................</p>
<p style="margin-top: 60px;">Poniman</p>
<p>....................................</p>
</div>
</body>
</html>
</html>

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Reset Password - UD.DNL PUTRA</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-blue-200 flex items-center justify-center h-screen">
<div class="bg-slate-400 p-6 rounded shadow-md w-full max-w-sm">
<h2 class="text-center text-white font-bold mb-4 bg-slate-600 py-2 rounded">RESET PASSWORD<br>UD.DNL PUTRA</h2>
@if ($errors->any())
<div class="bg-red-100 text-red-800 p-2 rounded mb-3 text-sm">
{{ $errors->first() }}
</div>
@endif
<form method="POST" action="{{ route('password.update') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<input type="hidden" name="email" value="{{ $email }}">
<input type="password" name="password" placeholder="Password Baru"
class="w-full px-3 py-2 rounded mb-3 bg-slate-500 text-white placeholder-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"
required>
<input type="password" name="password_confirmation" placeholder="Konfirmasi Password"
class="w-full px-3 py-2 rounded mb-3 bg-slate-500 text-white placeholder-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"
required>
<button type="submit"
class="w-full bg-slate-700 text-white py-2 rounded hover:bg-slate-800">
Reset Password
</button>
</form>
<div class="mt-3 text-center text-sm">
<a href="{{ route('login') }}" class="text-blue-100 hover:underline">Kembali ke login</a>
</div>
</div>
</body>
</html>

View File

@ -39,8 +39,13 @@
use Illuminate\Support\Facades\Route;
use App\Mail\TestMail;
use Illuminate\Support\Facades\Mail;
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\RegisterController;
use App\Http\Controllers\Auth\ForgotPasswordController;
use App\Http\Controllers\Auth\ResetPasswordController;
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\PengeluaranController;
use App\Http\Controllers\AbsenController;
@ -56,6 +61,14 @@
use App\Http\Controllers\TonIkanController;
use App\Http\Controllers\TransaksiController;
Route::get('/welcome', function () {
return view('welcome');
})->name('welcome');
Route::get('/tes-email', function () {
Mail::to('izzulhaqzaindimad@gmail.com')->send(new TestMail());
return 'Email dikirim!';
});
// rute untuk Auth
Route::match(['get', 'post'], '/', function (\Illuminate\Http\Request $request) {
@ -73,6 +86,12 @@
return app(RegisterController::class)->create($request);
})->name('register');
Route::get('password/forgot', [ForgotPasswordController::class, 'showLinkRequestForm'])->name('password.request');
Route::post('password/email', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('password.email');
Route::get('password/reset/{token}', [ResetPasswordController::class, 'showResetForm'])->name('password.reset');
Route::post('password/reset', [ResetPasswordController::class, 'reset'])->name('password.update');
Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])->name('logout');
// Pimpinan
@ -130,6 +149,9 @@
Route::post('/presensi/{id}/masuk', [PresensiController::class, 'inputMasuk'])->name('presensi.masuk');
Route::post('/presensi/{id}/pulang', [PresensiController::class, 'inputPulang'])->name('presensi.pulang');
Route::post('/presensi/update-jam-masuk', [PresensiController::class, 'updateJamMasukAjax'])->name('presensi.updateJamMasukAjax');
Route::post('/presensi/update-jam-Pulang', [PresensiController::class, 'updateJamPulangAjax'])->name('presensi.updateJamPulangAjax');
Route::post('/presensi/tonikan/store', [PresensiController::class, 'simpanTonIkan'])->name('presensi.tonikan.store');
// Route::post('/presensi/tonikan/store', [PresensiController::class, 'simpanTonIkan'])->name('presensi.tonikan.store');
@ -164,6 +186,8 @@
// export
Route::get('/gaji/kloter/{id}/export', [GajiController::class, 'export'])->name('gaji.kloter.export');
Route::get('operator/transaksi/export', [TransaksiController::class, 'exportExcel'])->name('operator.transaksi.export');
Route::get('/laporan/transaksi/export-pdf', [TransaksiController::class, 'exportPDF'])->name('operator.transaksi.export_pdf');
// });