CRUD petugas/pegawai + filter-sort-rekap transaksi parkir + fix save member kartu + hilangkan alert ganti Swal-loading
This commit is contained in:
@@ -27,10 +27,18 @@ public function index()
|
|||||||
* - tgl_awal / tgl_akhir (Y-m-d): periode kolom tanggal
|
* - tgl_awal / tgl_akhir (Y-m-d): periode kolom tanggal
|
||||||
* - status_kendaraan: 1 = masuk, 0 = keluar
|
* - status_kendaraan: 1 = masuk, 0 = keluar
|
||||||
* - status_transaksi: 0 = casual, 3 = member, 32 = denda
|
* - status_transaksi: 0 = casual, 3 = member, 32 = denda
|
||||||
|
* - sort_by + sort_dir (asc/desc): klik header kolom
|
||||||
* - export=csv: unduh hasil filter sebagai CSV
|
* - export=csv: unduh hasil filter sebagai CSV
|
||||||
*/
|
*/
|
||||||
public function data(Request $request)
|
public function data(Request $request)
|
||||||
{
|
{
|
||||||
|
$sortable = [
|
||||||
|
'id', 'no_pol', 'no_barcode', 'id_kendaraan', 'nama_kendaraan',
|
||||||
|
'waktu_masuk', 'waktu_keluar', 'tanggal',
|
||||||
|
'id_op_masuk', 'id_op_keluar', 'id_pintu_masuk', 'id_pintu_keluar',
|
||||||
|
'bayar_keluar', 'status', 'status_transaksi',
|
||||||
|
];
|
||||||
|
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'field' => 'nullable|in:id,no_pol,no_barcode',
|
'field' => 'nullable|in:id,no_pol,no_barcode',
|
||||||
'q' => 'nullable|string|max:100',
|
'q' => 'nullable|string|max:100',
|
||||||
@@ -38,6 +46,8 @@ public function data(Request $request)
|
|||||||
'tgl_akhir' => 'nullable|date_format:Y-m-d|after_or_equal:tgl_awal',
|
'tgl_akhir' => 'nullable|date_format:Y-m-d|after_or_equal:tgl_awal',
|
||||||
'status_kendaraan' => 'nullable|in:0,1',
|
'status_kendaraan' => 'nullable|in:0,1',
|
||||||
'status_transaksi' => 'nullable|in:0,3,32',
|
'status_transaksi' => 'nullable|in:0,3,32',
|
||||||
|
'sort_by' => 'nullable|in:' . implode(',', $sortable),
|
||||||
|
'sort_dir' => 'nullable|in:asc,desc',
|
||||||
'per_page' => 'nullable|integer|min:1|max:500',
|
'per_page' => 'nullable|integer|min:1|max:500',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -53,6 +63,7 @@ public function data(Request $request)
|
|||||||
return response()->json([
|
return response()->json([
|
||||||
'status' => true,
|
'status' => true,
|
||||||
'data' => $rows->items(),
|
'data' => $rows->items(),
|
||||||
|
'summary' => $this->summary($request),
|
||||||
'meta' => [
|
'meta' => [
|
||||||
'current_page' => $rows->currentPage(),
|
'current_page' => $rows->currentPage(),
|
||||||
'per_page' => $rows->perPage(),
|
'per_page' => $rows->perPage(),
|
||||||
@@ -62,6 +73,45 @@ public function data(Request $request)
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rekap sedetail logic report (per segmen) dari filter yang sedang aktif.
|
||||||
|
* - keluar = status 0 ; tunai = cara_bayar null/0 ; cashless = cara_bayar 3
|
||||||
|
* - casual/member/denda/manual/masalah = subset tunai (ikut reportOfficer)
|
||||||
|
*/
|
||||||
|
protected function summary(Request $request): array
|
||||||
|
{
|
||||||
|
$tunai = "(tp.cara_bayar IS NULL OR tp.cara_bayar = 0)";
|
||||||
|
$keluar = "tp.status = 0";
|
||||||
|
|
||||||
|
$query = $this->filteredQuery($request);
|
||||||
|
$query->reorder();
|
||||||
|
$query->columns = [];
|
||||||
|
$row = $query
|
||||||
|
->selectRaw("
|
||||||
|
count(*) as total_rows,
|
||||||
|
coalesce(sum(tp.bayar_keluar), 0) as total_bayar,
|
||||||
|
count(case when {$keluar} then 1 end) as keluar_rows,
|
||||||
|
coalesce(sum(case when {$keluar} then tp.bayar_keluar end), 0) as keluar_bayar,
|
||||||
|
count(case when {$keluar} and {$tunai} then 1 end) as tunai_rows,
|
||||||
|
coalesce(sum(case when {$keluar} and {$tunai} then tp.bayar_keluar end), 0) as tunai_bayar,
|
||||||
|
count(case when {$keluar} and tp.cara_bayar = 3 then 1 end) as cashless_rows,
|
||||||
|
coalesce(sum(case when {$keluar} and tp.cara_bayar = 3 then tp.bayar_keluar end), 0) as cashless_bayar,
|
||||||
|
count(case when {$keluar} and {$tunai} and tp.status_transaksi = '0' and tp.id_op_masuk is not null then 1 end) as casual_rows,
|
||||||
|
coalesce(sum(case when {$keluar} and {$tunai} and tp.status_transaksi = '0' and tp.id_op_masuk is not null then tp.bayar_keluar end), 0) as casual_bayar,
|
||||||
|
count(case when {$keluar} and {$tunai} and tp.status_transaksi = '3' then 1 end) as member_rows,
|
||||||
|
coalesce(sum(case when {$keluar} and {$tunai} and tp.status_transaksi = '3' then tp.bayar_keluar end), 0) as member_bayar,
|
||||||
|
count(case when {$keluar} and {$tunai} and tp.status_transaksi = '32' then 1 end) as denda_rows,
|
||||||
|
coalesce(sum(case when {$keluar} and {$tunai} and tp.status_transaksi = '32' then tp.bayar_keluar end), 0) as denda_bayar,
|
||||||
|
count(case when {$keluar} and {$tunai} and tp.id_op_masuk is null then 1 end) as manual_rows,
|
||||||
|
coalesce(sum(case when {$keluar} and {$tunai} and tp.id_op_masuk is null then tp.bayar_keluar end), 0) as manual_bayar,
|
||||||
|
count(case when {$keluar} and {$tunai} and tp.status_transaksi = '-1' then 1 end) as masalah_rows,
|
||||||
|
coalesce(sum(case when {$keluar} and {$tunai} and tp.status_transaksi = '-1' then tp.bayar_keluar end), 0) as masalah_bayar
|
||||||
|
")
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return (array) $row;
|
||||||
|
}
|
||||||
|
|
||||||
protected function filteredQuery(Request $request)
|
protected function filteredQuery(Request $request)
|
||||||
{
|
{
|
||||||
$query = DB::table('transaksi_parkir as tp')
|
$query = DB::table('transaksi_parkir as tp')
|
||||||
@@ -76,7 +126,12 @@ protected function filteredQuery(Request $request)
|
|||||||
'tp.status_transaksi',
|
'tp.status_transaksi',
|
||||||
'tp.waktu_masuk',
|
'tp.waktu_masuk',
|
||||||
'tp.waktu_keluar',
|
'tp.waktu_keluar',
|
||||||
'tp.tanggal'
|
'tp.tanggal',
|
||||||
|
'tp.id_op_masuk',
|
||||||
|
'tp.id_op_keluar',
|
||||||
|
'tp.id_pintu_masuk',
|
||||||
|
'tp.id_pintu_keluar',
|
||||||
|
'tp.bayar_keluar'
|
||||||
);
|
);
|
||||||
|
|
||||||
// 1. Pencarian: id / no_pol / no_barcode
|
// 1. Pencarian: id / no_pol / no_barcode
|
||||||
@@ -103,7 +158,13 @@ protected function filteredQuery(Request $request)
|
|||||||
$query->where('tp.status_transaksi', (int) $request->input('status_transaksi'));
|
$query->where('tp.status_transaksi', (int) $request->input('status_transaksi'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query->orderBy('tp.tanggal', 'desc');
|
// 5. Sorting klik header (default: tanggal terbaru)
|
||||||
|
$sortMap = ['nama_kendaraan' => 'jm.nama'];
|
||||||
|
$sortBy = $request->input('sort_by', 'tanggal');
|
||||||
|
$sortDir = $request->input('sort_dir', 'desc');
|
||||||
|
$query->orderBy($sortMap[$sortBy] ?? "tp.{$sortBy}", $sortDir);
|
||||||
|
|
||||||
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function exportCsv($query): StreamedResponse
|
protected function exportCsv($query): StreamedResponse
|
||||||
@@ -112,7 +173,7 @@ protected function exportCsv($query): StreamedResponse
|
|||||||
|
|
||||||
return response()->streamDownload(function () use ($query) {
|
return response()->streamDownload(function () use ($query) {
|
||||||
$out = fopen('php://output', 'w');
|
$out = fopen('php://output', 'w');
|
||||||
fputcsv($out, ['ID', 'No. Pol', 'Barcode', 'Kendaraan', 'Status Kendaraan', 'Status Transaksi', 'Waktu Masuk', 'Waktu Keluar']);
|
fputcsv($out, ['ID', 'No. Pol', 'Barcode', 'Kendaraan', 'Status Kendaraan', 'Status Transaksi', 'Waktu Masuk', 'Waktu Keluar', 'Op Masuk', 'Op Keluar', 'Pintu Masuk', 'Pintu Keluar', 'Bayar Keluar']);
|
||||||
$query->chunk(1000, function ($rows) use ($out) {
|
$query->chunk(1000, function ($rows) use ($out) {
|
||||||
foreach ($rows as $r) {
|
foreach ($rows as $r) {
|
||||||
fputcsv($out, [
|
fputcsv($out, [
|
||||||
@@ -124,6 +185,11 @@ protected function exportCsv($query): StreamedResponse
|
|||||||
$r->status_transaksi,
|
$r->status_transaksi,
|
||||||
$r->waktu_masuk,
|
$r->waktu_masuk,
|
||||||
$r->waktu_keluar,
|
$r->waktu_keluar,
|
||||||
|
$r->id_op_masuk,
|
||||||
|
$r->id_op_keluar,
|
||||||
|
$r->id_pintu_masuk,
|
||||||
|
$r->id_pintu_keluar,
|
||||||
|
$r->bayar_keluar,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Parking\Setup\Petugas;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Models\Pegawai;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class MasterPetugasController extends Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$petugas = Pegawai::orderBy('nomer')->get();
|
||||||
|
$levels = DB::table('level_user')->orderBy('level_code')->get();
|
||||||
|
|
||||||
|
return view('setup.petugas.petugas', compact('petugas', 'levels'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a newly created resource in storage.
|
||||||
|
*/
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$data = $request->validate($this->rules(), $this->messages());
|
||||||
|
// Normalisasi: string kosong jadi null (kolom date/int tidak terima '').
|
||||||
|
$data = array_map(fn ($v) => $v === '' ? null : $v, $data);
|
||||||
|
|
||||||
|
$pegawai = Pegawai::create(collect($data)->except('foto')->all());
|
||||||
|
|
||||||
|
if ($request->hasFile('foto')) {
|
||||||
|
$this->saveFoto($pegawai->id, $request->file('foto'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Petugas berhasil ditambahkan.',
|
||||||
|
'data' => $pegawai->fresh()->makeHidden('foto'),
|
||||||
|
], 201);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the specified resource (tanpa binary foto).
|
||||||
|
*/
|
||||||
|
public function show($id)
|
||||||
|
{
|
||||||
|
$pegawai = Pegawai::select(
|
||||||
|
'id', 'nomer', 'nama', 'alamat', 'tgl_lahir', 'tgl_masuk',
|
||||||
|
'username', 'password', 'bisalogin', 'status', 'jeniskelamin',
|
||||||
|
'agama', 'telepon', 'alamat_lahir', 'alamat_sekarang', 'nip',
|
||||||
|
'statusabsen', 'level_pegawai', 'email'
|
||||||
|
)->findOrFail($id);
|
||||||
|
|
||||||
|
$hasFoto = (bool) DB::table('pegawai')->where('id', $id)->whereNotNull('foto')->exists();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $pegawai,
|
||||||
|
'has_foto' => $hasFoto,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the specified resource in storage.
|
||||||
|
*/
|
||||||
|
public function update(Request $request, $id)
|
||||||
|
{
|
||||||
|
$pegawai = Pegawai::findOrFail($id);
|
||||||
|
|
||||||
|
$data = $request->validate($this->rules($pegawai->id), $this->messages());
|
||||||
|
// Normalisasi: string kosong jadi null (kolom date/int tidak terima '').
|
||||||
|
$data = array_map(fn ($v) => $v === '' ? null : $v, $data);
|
||||||
|
|
||||||
|
// Password kosong = tidak diubah (tetap pakai yang lama, plain-text ikut gaya legacy).
|
||||||
|
if (empty($data['password'])) {
|
||||||
|
unset($data['password']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pegawai->update(collect($data)->except('foto')->all());
|
||||||
|
|
||||||
|
if ($request->hasFile('foto')) {
|
||||||
|
$this->saveFoto($pegawai->id, $request->file('foto'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Petugas berhasil diperbarui.',
|
||||||
|
'data' => $pegawai->fresh()->makeHidden('foto'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified resource from storage.
|
||||||
|
*/
|
||||||
|
public function destroy($id)
|
||||||
|
{
|
||||||
|
Pegawai::findOrFail($id)->delete();
|
||||||
|
|
||||||
|
return response()->json(['success' => true, 'message' => 'Petugas berhasil dihapus.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream foto dari kolom bytea. Fallback ke placeholder bila kosong.
|
||||||
|
*/
|
||||||
|
public function foto($id)
|
||||||
|
{
|
||||||
|
$row = DB::table('pegawai')->select('foto')->where('id', $id)->first();
|
||||||
|
|
||||||
|
$binary = $row?->foto;
|
||||||
|
if (is_resource($binary)) {
|
||||||
|
$binary = stream_get_contents($binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($binary)) {
|
||||||
|
return response()->file(public_path('images/no-image.jpg'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$mime = (new \finfo(FILEINFO_MIME_TYPE))->buffer($binary) ?: 'image/jpeg';
|
||||||
|
|
||||||
|
return response($binary, 200, [
|
||||||
|
'Content-Type' => $mime,
|
||||||
|
'Content-Length' => strlen($binary),
|
||||||
|
'Cache-Control' => 'private, max-age=3600',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'nomer.unique' => 'Nomer sudah dipakai petugas lain, ganti dengan yang unik.',
|
||||||
|
'username.unique' => 'Username sudah dipakai petugas lain, ganti dengan yang unik.',
|
||||||
|
'foto.image' => 'File foto harus berupa gambar.',
|
||||||
|
'foto.max' => 'Ukuran foto maksimal 2MB.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function rules(?int $ignoreId = null): array
|
||||||
|
{
|
||||||
|
$uniqueUsername = Rule::unique('pegawai', 'username');
|
||||||
|
$uniqueNomer = Rule::unique('pegawai', 'nomer');
|
||||||
|
if ($ignoreId) {
|
||||||
|
$uniqueUsername->ignore($ignoreId, 'id');
|
||||||
|
$uniqueNomer->ignore($ignoreId, 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'nomer' => ['required', 'string', 'max:50', $uniqueNomer],
|
||||||
|
'nama' => 'required|string|max:50',
|
||||||
|
'username' => ['required', 'string', 'max:50', $uniqueUsername],
|
||||||
|
// Plain-text ikut gaya data legacy. Wajib saat tambah, opsional saat ubah.
|
||||||
|
'password' => ($ignoreId ? 'nullable' : 'required') . '|string|max:50',
|
||||||
|
'level_pegawai' => 'nullable|string|exists:level_user,level_code',
|
||||||
|
'bisalogin' => 'nullable|integer|in:0,1',
|
||||||
|
'status' => 'nullable|integer|in:0,1',
|
||||||
|
'jeniskelamin' => 'nullable|integer|in:0,1',
|
||||||
|
'statusabsen' => 'nullable|integer|in:0,1',
|
||||||
|
'agama' => 'nullable|string|max:50',
|
||||||
|
'telepon' => 'nullable|string|max:255',
|
||||||
|
'alamat' => 'nullable|string|max:100',
|
||||||
|
'alamat_lahir' => 'nullable|string|max:255',
|
||||||
|
'alamat_sekarang' => 'nullable|string|max:255',
|
||||||
|
'nip' => 'nullable|string|max:50',
|
||||||
|
'email' => 'nullable|string|max:64',
|
||||||
|
'tgl_lahir' => 'nullable|date',
|
||||||
|
'tgl_masuk' => 'nullable|date',
|
||||||
|
'foto' => 'nullable|image|mimes:jpg,jpeg,png,bmp|max:2048',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simpan binary ke kolom bytea tanpa nambah kolom baru.
|
||||||
|
* bin2hex menghindari masalah escaping bytea.
|
||||||
|
*/
|
||||||
|
protected function saveFoto(int $id, $file): void
|
||||||
|
{
|
||||||
|
$binary = file_get_contents($file->getRealPath());
|
||||||
|
|
||||||
|
DB::table('pegawai')->where('id', $id)->update([
|
||||||
|
'foto' => DB::raw("decode('" . bin2hex($binary) . "', 'hex')"),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,13 +115,6 @@ public function update(Request $request, $id)
|
|||||||
$data['denda_only'] = $request->boolean('denda_only') ? 1 : 0;
|
$data['denda_only'] = $request->boolean('denda_only') ? 1 : 0;
|
||||||
|
|
||||||
$dataQuery->fill($data);
|
$dataQuery->fill($data);
|
||||||
|
|
||||||
if (!$dataQuery->isDirty()) {
|
|
||||||
return response()->json([
|
|
||||||
"status" => false,
|
|
||||||
"message" => "Tidak ada perubahan data",
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$dataQuery->save();
|
$dataQuery->save();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class Pegawai extends Model implements AuthenticatableContract
|
|||||||
{
|
{
|
||||||
protected $table = 'pegawai'; // Nama tabel
|
protected $table = 'pegawai'; // Nama tabel
|
||||||
protected $primaryKey = 'id'; // Primary key tabel
|
protected $primaryKey = 'id'; // Primary key tabel
|
||||||
|
public $timestamps = false; // Tabel tidak punya created_at / updated_at
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'nomer', 'nama', 'alamat', 'tgl_lahir', 'tgl_masuk', 'username', 'password', 'foto', 'bisalogin', 'status', 'jeniskelamin', 'agama', 'telepon', 'alamat_lahir', 'alamat_sekarang', 'nip', 'statusabsen', 'level_pegawai'
|
'nomer', 'nama', 'alamat', 'tgl_lahir', 'tgl_masuk', 'username', 'password', 'foto', 'bisalogin', 'status', 'jeniskelamin', 'agama', 'telepon', 'alamat_lahir', 'alamat_sekarang', 'nip', 'statusabsen', 'level_pegawai'
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public function stikerCreate($request, Carbon $awal, Carbon $akhir)
|
|||||||
if (!empty($dataCard)) {
|
if (!empty($dataCard)) {
|
||||||
foreach ($dataCard as $cardNumber) {
|
foreach ($dataCard as $cardNumber) {
|
||||||
$insertCard = [
|
$insertCard = [
|
||||||
"notrans" => $cardNumber["no_id"],
|
"notrans" => $cardNumber["notrans"],
|
||||||
"no_card" => $cardNumber["nocard"],
|
"no_card" => $cardNumber["nocard"],
|
||||||
"no_urut" => $cardNumber["nourut"],
|
"no_urut" => $cardNumber["nourut"],
|
||||||
];
|
];
|
||||||
@@ -99,7 +99,7 @@ public function stikerCreate($request, Carbon $awal, Carbon $akhir)
|
|||||||
"status" => true,
|
"status" => true,
|
||||||
"message" => "Data berhasil di simpan",
|
"message" => "Data berhasil di simpan",
|
||||||
]);
|
]);
|
||||||
} catch (Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
"status" => false,
|
"status" => false,
|
||||||
"message" => "Gagal menyimpan data " . $e->getMessage() . $e->getLine(),
|
"message" => "Gagal menyimpan data " . $e->getMessage() . $e->getLine(),
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ $(document).ready(function () {
|
|||||||
$(".nopol-add").next(".invalid-feedback").hide();
|
$(".nopol-add").next(".invalid-feedback").hide();
|
||||||
|
|
||||||
if (!vehicle) {
|
if (!vehicle) {
|
||||||
alert("Wajib pilih jenis kendaraan");
|
Swal.fire({icon: 'warning', title: 'Belum lengkap', text: "Wajib pilih jenis kendaraan"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ $(document).ready(function () {
|
|||||||
var rowhtml = `
|
var rowhtml = `
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">${nopol}</td>
|
<td class="text-center">${nopol}</td>
|
||||||
<td class="text-center" data-value="${vehicle}"">${vehicleName}</td>
|
<td class="text-center" data-value="${vehicle}">${vehicleName}</td>
|
||||||
<td class="text-center">${member}</td>
|
<td class="text-center">${member}</td>
|
||||||
<td class="text-center">${merk}</td>
|
<td class="text-center">${merk}</td>
|
||||||
<td class="text-center p-1" style="text-align: center;">
|
<td class="text-center p-1" style="text-align: center;">
|
||||||
@@ -144,8 +144,8 @@ $(document).ready(function () {
|
|||||||
console.log(r.message);
|
console.log(r.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function (xhr) {
|
||||||
alert("Terjadi kesalahan saat memeriksa nopol");
|
Swal.fire({icon: 'error', title: 'Gagal memeriksa', text: xhr.responseJSON?.message || "Terjadi kesalahan saat memeriksa nopol"});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -246,6 +246,9 @@ $(document).ready(function () {
|
|||||||
console.log("FORM ADD:", allData);
|
console.log("FORM ADD:", allData);
|
||||||
|
|
||||||
if ($(".tambah-member").valid()) {
|
if ($(".tambah-member").valid()) {
|
||||||
|
const saveBtn = tombol;
|
||||||
|
const saveHtml = saveBtn.html();
|
||||||
|
saveBtn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Menyimpan...');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: $("#add-member").attr("action"),
|
url: $("#add-member").attr("action"),
|
||||||
type: "POST",
|
type: "POST",
|
||||||
@@ -255,10 +258,11 @@ $(document).ready(function () {
|
|||||||
// tombol.attr("disabled", true);
|
// tombol.attr("disabled", true);
|
||||||
},
|
},
|
||||||
success: function (r) {
|
success: function (r) {
|
||||||
// alert('Data berhasil dikirim');
|
const ok = r.status == true || r.response == true;
|
||||||
if (r.response == true) {
|
if (ok) {
|
||||||
$("#modal-member").modal("hide");
|
$("#modal-member").modal("hide");
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
|
icon: "success",
|
||||||
title: "Berhasil!!",
|
title: "Berhasil!!",
|
||||||
text: "Member berhasil di daftarkan.. !!",
|
text: "Member berhasil di daftarkan.. !!",
|
||||||
timer: 5000,
|
timer: 5000,
|
||||||
@@ -267,7 +271,7 @@ $(document).ready(function () {
|
|||||||
// Reload halaman setelah SweetAlert ditutup
|
// Reload halaman setelah SweetAlert ditutup
|
||||||
// location.reload(); // Ini akan me-reload halaman
|
// location.reload(); // Ini akan me-reload halaman
|
||||||
});
|
});
|
||||||
} else if (r.response == false) {
|
} else {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
icon: "error",
|
icon: "error",
|
||||||
title: "Oops...",
|
title: "Oops...",
|
||||||
@@ -277,7 +281,10 @@ $(document).ready(function () {
|
|||||||
console.log(r);
|
console.log(r);
|
||||||
},
|
},
|
||||||
error: function (xhr, status, error) {
|
error: function (xhr, status, error) {
|
||||||
alert("Terjadi kesalahan: " + error);
|
Swal.fire({icon: 'error', title: 'Gagal menyimpan', text: xhr.responseJSON?.message || ("Terjadi kesalahan: " + error)});
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
saveBtn.prop('disabled', false).html(saveHtml);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('.btn-cancel').click();
|
$('.btn-cancel').click();
|
||||||
alert('Berhasil disimpan 🔥');
|
if (window.Swal) Swal.fire({icon: 'success', title: 'Berhasil', text: 'Konfigurasi hardware tersimpan.'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -34,10 +34,14 @@ $(document).ready(function () {
|
|||||||
$(document).on('click', '.vehicle-price', function () {
|
$(document).on('click', '.vehicle-price', function () {
|
||||||
|
|
||||||
|
|
||||||
let id = $(this).data('id');
|
let btn = $(this);
|
||||||
let nama = $(this).data('nama');
|
let btnHtml = btn.html();
|
||||||
|
let id = btn.data('id');
|
||||||
|
let nama = btn.data('nama');
|
||||||
console.log(id);
|
console.log(id);
|
||||||
|
|
||||||
|
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Memuat...');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/setup/master/price/' + id,
|
url: '/setup/master/price/' + id,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
@@ -70,6 +74,9 @@ $(document).ready(function () {
|
|||||||
},
|
},
|
||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
Swal.fire({icon: 'error', title: 'Gagal memuat tarif', text: xhr.responseJSON?.message || 'Data tarif tidak ditemukan.'});
|
Swal.fire({icon: 'error', title: 'Gagal memuat tarif', text: xhr.responseJSON?.message || 'Data tarif tidak ditemukan.'});
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
btn.prop('disabled', false).html(btnHtml);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -105,6 +112,10 @@ $(document).ready(function () {
|
|||||||
url = url.replace(':id', encodeURIComponent(id));
|
url = url.replace(':id', encodeURIComponent(id));
|
||||||
console.log(url);
|
console.log(url);
|
||||||
|
|
||||||
|
const saveBtn = $(this);
|
||||||
|
const saveHtml = saveBtn.html();
|
||||||
|
saveBtn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Menyimpan...');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
type: 'PUT',
|
type: 'PUT',
|
||||||
@@ -126,6 +137,9 @@ $(document).ready(function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Swal.fire({icon: 'error', title: 'Gagal memperbarui tarif', text: xhr.responseJSON?.message || 'Silakan periksa kembali data tarif.'});
|
Swal.fire({icon: 'error', title: 'Gagal memperbarui tarif', text: xhr.responseJSON?.message || 'Silakan periksa kembali data tarif.'});
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
saveBtn.prop('disabled', false).html(saveHtml);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ $(document).ready(function () {
|
|||||||
$(".nopol-add").next(".invalid-feedback").hide();
|
$(".nopol-add").next(".invalid-feedback").hide();
|
||||||
|
|
||||||
if (!vehicle) {
|
if (!vehicle) {
|
||||||
alert("Wajib pilih jenis kendaraan");
|
Swal.fire({icon: 'warning', title: 'Belum lengkap', text: "Wajib pilih jenis kendaraan"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ $(document).ready(function () {
|
|||||||
var rowhtml = `
|
var rowhtml = `
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center">${nopol}</td>
|
<td class="text-center">${nopol}</td>
|
||||||
<td class="text-center" data-value="${vehicle}"">${vehicleName}</td>
|
<td class="text-center" data-value="${vehicle}">${vehicleName}</td>
|
||||||
<td class="text-center">${member}</td>
|
<td class="text-center">${member}</td>
|
||||||
<td class="text-center">${merk}</td>
|
<td class="text-center">${merk}</td>
|
||||||
<td class="text-center p-1" style="text-align: center;">
|
<td class="text-center p-1" style="text-align: center;">
|
||||||
@@ -144,8 +144,8 @@ $(document).ready(function () {
|
|||||||
console.log(r.message);
|
console.log(r.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function (xhr) {
|
||||||
alert("Terjadi kesalahan saat memeriksa nopol");
|
Swal.fire({icon: 'error', title: 'Gagal memeriksa', text: xhr.responseJSON?.message || "Terjadi kesalahan saat memeriksa nopol"});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -244,21 +244,23 @@ $(document).ready(function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ($(".tambah-member").valid()) {
|
if ($(".tambah-member").valid()) {
|
||||||
|
const saveHtml = tombol.html();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: $("#add-member").attr("action"),
|
url: $("#add-member").attr("action"),
|
||||||
type: "POST",
|
type: "POST",
|
||||||
data: allData,
|
data: allData,
|
||||||
headers: { "X-CSRF-token": $('meta[name="csrf-token"]').attr("content") },
|
headers: { "X-CSRF-token": $('meta[name="csrf-token"]').attr("content") },
|
||||||
beforeSend: function () {
|
beforeSend: function () {
|
||||||
tombol.attr("disabled", true);
|
tombol.prop("disabled", true).html('<span class="spinner-border spinner-border-sm me-1"></span>Menyimpan...');
|
||||||
},
|
},
|
||||||
success: function (r) {
|
success: function (r) {
|
||||||
// alert('Data berhasil dikirim');
|
// alert('Data berhasil dikirim');
|
||||||
$("#modal-member").modal("hide");
|
$("#modal-member").modal("hide");
|
||||||
if (r.status == true) {
|
if (r.status == true) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
|
icon: "success",
|
||||||
title: "Berhasil!!",
|
title: "Berhasil!!",
|
||||||
text: "Member anda telah terdaftar !!",
|
text: r.message || "Member anda telah terdaftar !!",
|
||||||
timer: 5000,
|
timer: 5000,
|
||||||
showConfirmButton: !1,
|
showConfirmButton: !1,
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
@@ -266,12 +268,15 @@ $(document).ready(function () {
|
|||||||
location.reload(); // Ini akan me-reload halaman
|
location.reload(); // Ini akan me-reload halaman
|
||||||
});
|
});
|
||||||
} else if (r.status == false) {
|
} else if (r.status == false) {
|
||||||
Swal.fire("Oops...", "Terjadi kendala !!", "silahkan hubungi admin", "error");
|
Swal.fire({icon: "error", title: "Oops...", text: r.message || "Terjadi kendala, silakan hubungi admin."});
|
||||||
}
|
}
|
||||||
console.log(r);
|
console.log(r);
|
||||||
},
|
},
|
||||||
error: function (xhr, status, error) {
|
error: function (xhr, status, error) {
|
||||||
alert("Terjadi kesalahan: " + error);
|
Swal.fire({icon: 'error', title: 'Gagal menyimpan', text: xhr.responseJSON?.message || ("Terjadi kesalahan: " + error)});
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
tombol.prop('disabled', false).html(saveHtml);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,5 +139,6 @@
|
|||||||
// tidak hardcode URL (menghindari masalah beda host/port seperti sebelumnya).
|
// tidak hardcode URL (menghindari masalah beda host/port seperti sebelumnya).
|
||||||
window.STASIUNKERJA_ENDPOINT = "{{ url('/setup/master/stasiunkerja') }}";
|
window.STASIUNKERJA_ENDPOINT = "{{ url('/setup/master/stasiunkerja') }}";
|
||||||
</script>
|
</script>
|
||||||
|
<script src="{{ asset('vendor/sweetalert2/dist/sweetalert2.min.js') }}"></script>
|
||||||
<script src="{{ asset('js/setup/stasiunkerja.js') }}"></script>
|
<script src="{{ asset('js/setup/stasiunkerja.js') }}"></script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -146,5 +146,6 @@
|
|||||||
<script>
|
<script>
|
||||||
window.TARIFSTIKER_ENDPOINT = "{{ url('/setup/master/tarif-stiker') }}";
|
window.TARIFSTIKER_ENDPOINT = "{{ url('/setup/master/tarif-stiker') }}";
|
||||||
</script>
|
</script>
|
||||||
|
<script src="{{ asset('vendor/sweetalert2/dist/sweetalert2.min.js') }}"></script>
|
||||||
<script src="{{ asset('js/setup/tarifstiker.js') }}"></script>
|
<script src="{{ asset('js/setup/tarifstiker.js') }}"></script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<li class="nav-item"><a class="nav-link" href="/transaksi/member">Master Shift</a></li>
|
<li class="nav-item"><a class="nav-link" href="/transaksi/member">Master Shift</a></li>
|
||||||
<hr>
|
<hr>
|
||||||
<li class="nav-item"><a class="nav-link" href="/setup/master/level-user">Master Level Pengguna</a></li>
|
<li class="nav-item"><a class="nav-link" href="/setup/master/level-user">Master Level Pengguna</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="/transaksi/member">Master Petugas/Pegawai</a></li>
|
<li class="nav-item"><a class="nav-link" href="/setup/master/petugas">Master Petugas/Pegawai</a></li>
|
||||||
<hr>
|
<hr>
|
||||||
<li class="nav-item"><a class="nav-link" href="/transaksi/member">Master Stiker</a></li>
|
<li class="nav-item"><a class="nav-link" href="/transaksi/member">Master Stiker</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -25,6 +25,17 @@
|
|||||||
.table td {
|
.table td {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
} */
|
} */
|
||||||
|
#tabel-parkir th[data-sort] {
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
#tabel-parkir th[data-sort]:hover {
|
||||||
|
background-color: #f2f6ff;
|
||||||
|
}
|
||||||
|
#tabel-parkir .sort-ind {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@@ -117,6 +128,17 @@ class="btn btn-sm btn-icon btn-danger btn-export-pdf" title="Cetak">
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<hr class="my-3">
|
<hr class="my-3">
|
||||||
|
<div class="row g-2 mb-3" id="rekap-parkir">
|
||||||
|
<div class="col-md-3"><div class="border rounded p-2 h-100"><small class="text-muted">Total (filter aktif)</small><div class="fw-bold" id="sum-total">-</div><small class="text-muted" id="sum-total-rp"></small></div></div>
|
||||||
|
<div class="col-md-3"><div class="border rounded p-2 h-100"><small class="text-muted">Keluar (status 0)</small><div class="fw-bold" id="sum-keluar">-</div><small class="text-muted" id="sum-keluar-rp"></small></div></div>
|
||||||
|
<div class="col-md-3"><div class="border rounded p-2 h-100"><small class="text-muted">Tunai</small><div class="fw-bold text-success" id="sum-tunai">-</div><small class="text-muted" id="sum-tunai-rp"></small></div></div>
|
||||||
|
<div class="col-md-3"><div class="border rounded p-2 h-100"><small class="text-muted">Cashless</small><div class="fw-bold text-info" id="sum-cashless">-</div><small class="text-muted" id="sum-cashless-rp"></small></div></div>
|
||||||
|
<div class="col-md-2"><div class="border rounded p-2 h-100"><small class="text-muted">Casual</small><div class="fw-bold" id="sum-casual">-</div><small class="text-muted" id="sum-casual-rp"></small></div></div>
|
||||||
|
<div class="col-md-2"><div class="border rounded p-2 h-100"><small class="text-muted">Member</small><div class="fw-bold" id="sum-member">-</div><small class="text-muted" id="sum-member-rp"></small></div></div>
|
||||||
|
<div class="col-md-2"><div class="border rounded p-2 h-100"><small class="text-muted">Denda</small><div class="fw-bold" id="sum-denda">-</div><small class="text-muted" id="sum-denda-rp"></small></div></div>
|
||||||
|
<div class="col-md-3"><div class="border rounded p-2 h-100"><small class="text-muted">Manual</small><div class="fw-bold" id="sum-manual">-</div><small class="text-muted" id="sum-manual-rp"></small></div></div>
|
||||||
|
<div class="col-md-3"><div class="border rounded p-2 h-100"><small class="text-muted">Masalah (-1)</small><div class="fw-bold text-warning" id="sum-masalah">-</div><small class="text-muted" id="sum-masalah-rp"></small></div></div>
|
||||||
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||||
<h5 class="card-title mb-0">Hasil Transaksi</h5>
|
<h5 class="card-title mb-0">Hasil Transaksi</h5>
|
||||||
<small class="text-muted" id="info-paging"></small>
|
<small class="text-muted" id="info-paging"></small>
|
||||||
@@ -126,18 +148,23 @@ class="btn btn-sm btn-icon btn-danger btn-export-pdf" title="Cetak">
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th width="4%">No.</th>
|
<th width="4%">No.</th>
|
||||||
<th>ID</th>
|
<th data-sort="id">ID <span class="sort-ind"></span></th>
|
||||||
<th>No. Pol</th>
|
<th data-sort="no_pol">No. Pol <span class="sort-ind"></span></th>
|
||||||
<th>Barcode</th>
|
<th data-sort="no_barcode">Barcode <span class="sort-ind"></span></th>
|
||||||
<th>Kendaraan</th>
|
<th data-sort="nama_kendaraan">Kendaraan <span class="sort-ind"></span></th>
|
||||||
<th>Waktu Masuk</th>
|
<th data-sort="waktu_masuk">Waktu Masuk <span class="sort-ind"></span></th>
|
||||||
<th>Waktu Keluar</th>
|
<th data-sort="waktu_keluar">Waktu Keluar <span class="sort-ind"></span></th>
|
||||||
<th>Status Kendaraan</th>
|
<th data-sort="id_op_masuk">Op Masuk <span class="sort-ind"></span></th>
|
||||||
<th>Status Transaksi</th>
|
<th data-sort="id_op_keluar">Op Keluar <span class="sort-ind"></span></th>
|
||||||
|
<th data-sort="id_pintu_masuk">Pintu Masuk <span class="sort-ind"></span></th>
|
||||||
|
<th data-sort="id_pintu_keluar">Pintu Keluar <span class="sort-ind"></span></th>
|
||||||
|
<th data-sort="bayar_keluar">Bayar Keluar <span class="sort-ind"></span></th>
|
||||||
|
<th data-sort="status">Status Kendaraan <span class="sort-ind"></span></th>
|
||||||
|
<th data-sort="status_transaksi">Status Transaksi <span class="sort-ind"></span></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="body-parkir">
|
<tbody id="body-parkir">
|
||||||
<tr><td colspan="9" class="text-center">Memuat data...</td></tr>
|
<tr><td colspan="14" class="text-center">Memuat data...</td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,6 +189,8 @@ class="btn btn-sm btn-icon btn-danger btn-export-pdf" title="Cetak">
|
|||||||
$(function () {
|
$(function () {
|
||||||
const dataUrl = '{{ route('transaksiparkir.data', [], false) }}';
|
const dataUrl = '{{ route('transaksiparkir.data', [], false) }}';
|
||||||
let page = 1;
|
let page = 1;
|
||||||
|
let sortBy = 'tanggal';
|
||||||
|
let sortDir = 'desc';
|
||||||
|
|
||||||
const labelKendaraan = {1: 'Masuk', 0: 'Keluar'};
|
const labelKendaraan = {1: 'Masuk', 0: 'Keluar'};
|
||||||
const labelTransaksi = {0: 'Casual', 3: 'Member', 32: 'Denda'};
|
const labelTransaksi = {0: 'Casual', 3: 'Member', 32: 'Denda'};
|
||||||
@@ -196,23 +225,62 @@ function params(extra) {
|
|||||||
tgl_akhir: $('#tgl_akhir').val(),
|
tgl_akhir: $('#tgl_akhir').val(),
|
||||||
status_kendaraan: $('#status_kendaraan').val(),
|
status_kendaraan: $('#status_kendaraan').val(),
|
||||||
status_transaksi: $('#status_transaksi').val(),
|
status_transaksi: $('#status_transaksi').val(),
|
||||||
|
sort_by: sortBy,
|
||||||
|
sort_dir: sortDir,
|
||||||
page: page
|
page: page
|
||||||
};
|
};
|
||||||
return $.param(Object.assign(p, extra || {}));
|
return $.param(Object.assign(p, extra || {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function paintSort() {
|
||||||
|
$('#tabel-parkir th[data-sort] .sort-ind').text('');
|
||||||
|
const th = $('#tabel-parkir th[data-sort="' + sortBy + '"] .sort-ind');
|
||||||
|
if (th.length) th.text(sortDir === 'asc' ? '▲' : '▼');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#tabel-parkir').on('click', 'th[data-sort]', function () {
|
||||||
|
const col = $(this).data('sort');
|
||||||
|
if (sortBy === col) {
|
||||||
|
sortDir = sortDir === 'asc' ? 'desc' : 'asc';
|
||||||
|
} else {
|
||||||
|
sortBy = col;
|
||||||
|
sortDir = 'asc';
|
||||||
|
}
|
||||||
|
page = 1;
|
||||||
|
paintSort();
|
||||||
|
load();
|
||||||
|
});
|
||||||
|
|
||||||
|
function fmtRp(v) {
|
||||||
|
if (v === null || v === undefined || v === '') return '';
|
||||||
|
return 'Rp ' + Number(v).toLocaleString('id-ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
function paintSummary(s) {
|
||||||
|
if (!s) return;
|
||||||
|
const segs = ['total', 'keluar', 'tunai', 'cashless', 'casual', 'member', 'denda', 'manual', 'masalah'];
|
||||||
|
segs.forEach(function (k) {
|
||||||
|
$('#sum-' + k).text((Number(s[k + '_rows']) || 0).toLocaleString('id-ID') + ' trx');
|
||||||
|
$('#sum-' + k + '-rp').text(fmtRp(s[k + '_bayar']));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
$('#body-parkir').html('<tr><td colspan="9" class="text-center">Memuat data...</td></tr>');
|
$('#body-parkir').html('<tr><td colspan="14" class="text-center text-muted py-4"><span class="spinner-border spinner-border-sm me-2"></span>Memuat data...</td></tr>');
|
||||||
|
$('#btn-cari').prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Mencari...');
|
||||||
|
$('#btn-prev, #btn-next').prop('disabled', true);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: dataUrl + '?' + params(),
|
url: dataUrl + '?' + params(),
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
success: function (r) {
|
success: function (r) {
|
||||||
|
paintSummary(r.summary);
|
||||||
if (!r.data.length) {
|
if (!r.data.length) {
|
||||||
$('#body-parkir').html('<tr><td colspan="9" class="text-center">Data tidak ditemukan.</td></tr>');
|
$('#body-parkir').html('<tr><td colspan="14" class="text-center">Data tidak ditemukan.</td></tr>');
|
||||||
} else {
|
} else {
|
||||||
let html = '';
|
let html = '';
|
||||||
const offset = (r.meta.current_page - 1) * r.meta.per_page;
|
const offset = (r.meta.current_page - 1) * r.meta.per_page;
|
||||||
r.data.forEach(function (row, i) {
|
r.data.forEach(function (row, i) {
|
||||||
|
const bayar = row.bayar_keluar === null || row.bayar_keluar === undefined || row.bayar_keluar === '' ? '' : Number(row.bayar_keluar).toLocaleString('id-ID');
|
||||||
html += '<tr>'
|
html += '<tr>'
|
||||||
+ '<td class="text-center">' + (offset + i + 1) + '</td>'
|
+ '<td class="text-center">' + (offset + i + 1) + '</td>'
|
||||||
+ '<td>' + esc(row.id) + '</td>'
|
+ '<td>' + esc(row.id) + '</td>'
|
||||||
@@ -221,6 +289,11 @@ function load() {
|
|||||||
+ '<td>' + esc([row.id_kendaraan, row.nama_kendaraan].filter(Boolean).join(' - ')) + '</td>'
|
+ '<td>' + esc([row.id_kendaraan, row.nama_kendaraan].filter(Boolean).join(' - ')) + '</td>'
|
||||||
+ '<td>' + esc(row.waktu_masuk) + '</td>'
|
+ '<td>' + esc(row.waktu_masuk) + '</td>'
|
||||||
+ '<td>' + esc(row.waktu_keluar) + '</td>'
|
+ '<td>' + esc(row.waktu_keluar) + '</td>'
|
||||||
|
+ '<td>' + esc(row.id_op_masuk) + '</td>'
|
||||||
|
+ '<td>' + esc(row.id_op_keluar) + '</td>'
|
||||||
|
+ '<td>' + esc(row.id_pintu_masuk) + '</td>'
|
||||||
|
+ '<td>' + esc(row.id_pintu_keluar) + '</td>'
|
||||||
|
+ '<td class="text-end">' + bayar + '</td>'
|
||||||
+ '<td class="text-center">' + statusKendaraan(row.status) + '</td>'
|
+ '<td class="text-center">' + statusKendaraan(row.status) + '</td>'
|
||||||
+ '<td class="text-center">' + statusTransaksi(row.status_transaksi) + '</td>'
|
+ '<td class="text-center">' + statusTransaksi(row.status_transaksi) + '</td>'
|
||||||
+ '</tr>';
|
+ '</tr>';
|
||||||
@@ -238,7 +311,11 @@ function load() {
|
|||||||
if (xhr.status === 422 && xhr.responseJSON?.errors) {
|
if (xhr.status === 422 && xhr.responseJSON?.errors) {
|
||||||
msg = Object.values(xhr.responseJSON.errors).flat().join(' ');
|
msg = Object.values(xhr.responseJSON.errors).flat().join(' ');
|
||||||
}
|
}
|
||||||
$('#body-parkir').html('<tr><td colspan="9" class="text-center text-danger">' + esc(msg) + '</td></tr>');
|
$('#body-parkir').html('<tr><td colspan="14" class="text-center text-danger">' + esc(msg) + '</td></tr>');
|
||||||
|
Swal.fire({icon: 'error', title: 'Gagal memuat data', text: msg});
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
$('#btn-cari').prop('disabled', false).html('<i class="fa fa-search me-1"></i>Cari');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -258,6 +335,7 @@ function load() {
|
|||||||
});
|
});
|
||||||
$('.btn-export-pdf').on('click', function () { window.print(); });
|
$('.btn-export-pdf').on('click', function () { window.print(); });
|
||||||
|
|
||||||
|
paintSort();
|
||||||
load();
|
load();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -178,7 +178,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('.btn-cancel').click();
|
$('.btn-cancel').click();
|
||||||
alert('Berhasil disimpan 🔥');
|
Swal.fire({icon: 'success', title: 'Berhasil', text: 'Level pengguna tersimpan.'});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -155,6 +155,8 @@
|
|||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
$(document).on('click', '.hardware-button', function () {
|
$(document).on('click', '.hardware-button', function () {
|
||||||
|
let btn = $(this);
|
||||||
|
let btnHtml = btn.html();
|
||||||
let row = $(this).closest('tr');
|
let row = $(this).closest('tr');
|
||||||
|
|
||||||
// kalau sudah ada baris edit, hapus dulu
|
// kalau sudah ada baris edit, hapus dulu
|
||||||
@@ -166,6 +168,8 @@
|
|||||||
// # base url tanpa parameter biar tidak dobel slash
|
// # base url tanpa parameter biar tidak dobel slash
|
||||||
let baseUrl = "{{ url('setup/master/gate-hardware') }}";
|
let baseUrl = "{{ url('setup/master/gate-hardware') }}";
|
||||||
|
|
||||||
|
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Memuat...');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: baseUrl + "/" + id,
|
url: baseUrl + "/" + id,
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
@@ -244,6 +248,10 @@ class="form-control form-control-sm edit-mode d-none"
|
|||||||
},
|
},
|
||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
console.error(xhr.responseText);
|
console.error(xhr.responseText);
|
||||||
|
Swal.fire({icon: 'error', title: 'Gagal memuat hardware', text: xhr.responseJSON?.message || 'Cek koneksi / hubungi admin.'});
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
btn.prop('disabled', false).html(btnHtml);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -275,6 +283,8 @@ class="form-control form-control-sm edit-mode d-none"
|
|||||||
|
|
||||||
// SAVE
|
// SAVE
|
||||||
$(document).on('click', '.btn-save', function () {
|
$(document).on('click', '.btn-save', function () {
|
||||||
|
const btn = $(this);
|
||||||
|
const origHtml = btn.html();
|
||||||
|
|
||||||
let dataToSend = {};
|
let dataToSend = {};
|
||||||
let gateId = $('.edit-row').data('id');
|
let gateId = $('.edit-row').data('id');
|
||||||
@@ -289,6 +299,8 @@ class="form-control form-control-sm edit-mode d-none"
|
|||||||
// # base url tanpa parameter biar tidak dobel slash
|
// # base url tanpa parameter biar tidak dobel slash
|
||||||
let baseUrl = "{{ url('setup/master/gate-hardware') }}";
|
let baseUrl = "{{ url('setup/master/gate-hardware') }}";
|
||||||
|
|
||||||
|
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Menyimpan...');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: baseUrl + "/" + gateId,
|
url: baseUrl + "/" + gateId,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
@@ -308,11 +320,14 @@ class="form-control form-control-sm edit-mode d-none"
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('.btn-cancel').click();
|
$('.btn-cancel').click();
|
||||||
alert('Berhasil disimpan');
|
Swal.fire({icon: 'success', title: 'Berhasil', text: 'Konfigurasi hardware tersimpan.'});
|
||||||
},
|
},
|
||||||
error: function (xhr) {
|
error: function (xhr) {
|
||||||
console.error(xhr.responseText);
|
console.error(xhr.responseText);
|
||||||
alert('Gagal menyimpan. Cek koneksi / hubungi admin.');
|
Swal.fire({icon: 'error', title: 'Gagal menyimpan', text: xhr.responseJSON?.message || 'Cek koneksi / hubungi admin.'});
|
||||||
|
},
|
||||||
|
complete: function () {
|
||||||
|
btn.prop('disabled', false).html(origHtml);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -598,6 +598,7 @@ class="btn btn-primary"
|
|||||||
logsUrl: @json(route('dhcp.logs')),
|
logsUrl: @json(route('dhcp.logs')),
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<script src="{{ asset('vendor/sweetalert2/dist/sweetalert2.min.js') }}"></script>
|
||||||
<script src="{{ asset('js/setup/dhcp.js') }}"></script>
|
<script src="{{ asset('js/setup/dhcp.js') }}"></script>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
@extends('layouts.master')
|
||||||
|
|
||||||
|
@section('styles')
|
||||||
|
<style>
|
||||||
|
.foto-thumb { width: 48px; height: 48px; object-fit: cover; border-radius: 6px; border: 1px solid #dee2e6; }
|
||||||
|
.foto-preview { width: 120px; height: 120px; object-fit: cover; border-radius: 8px; border: 1px solid #dee2e6; }
|
||||||
|
.card-body label { font-size: 13px; color: #6c757d; }
|
||||||
|
</style>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid page-body-wrapper">
|
||||||
|
<div class="main-panel">
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 grid-margin stretch-card">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h4 class="card-title mb-0">Master Petugas / Pegawai</h4>
|
||||||
|
<button class="btn btn-primary btn-sm" id="btnAdd" type="button">
|
||||||
|
<i class="fa fa-plus me-1"></i>Tambah
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-striped" id="petugas-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center" width="5%">Foto</th>
|
||||||
|
<th>Nomer</th>
|
||||||
|
<th>Nama</th>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>Level</th>
|
||||||
|
<th class="text-center">Bisa Login</th>
|
||||||
|
<th class="text-center">Status</th>
|
||||||
|
<th class="text-center" width="18%">Aksi</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse ($petugas as $p)
|
||||||
|
<tr>
|
||||||
|
<td class="text-center">
|
||||||
|
<img class="foto-thumb" src="{{ route('petugas.foto', $p->id, false) }}" alt="foto" loading="lazy">
|
||||||
|
</td>
|
||||||
|
<td>{{ $p->nomer }}</td>
|
||||||
|
<td>{{ $p->nama }}</td>
|
||||||
|
<td>{{ $p->username }}</td>
|
||||||
|
<td>{{ $levels->firstWhere('level_code', $p->level_pegawai)->level_name ?? $p->level_pegawai }}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge {{ $p->bisalogin == 1 ? 'bg-success' : 'bg-secondary' }}">
|
||||||
|
{{ $p->bisalogin == 1 ? 'Ya' : 'Tidak' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<span class="badge {{ $p->status == 1 ? 'bg-success' : 'bg-danger' }}">
|
||||||
|
{{ $p->status == 1 ? 'Aktif' : 'Nonaktif' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button class="btn btn-info btn-sm btn-detail" type="button" data-id="{{ $p->id }}">Detail</button>
|
||||||
|
<button class="btn btn-warning btn-sm btn-edit" type="button" data-id="{{ $p->id }}">Ubah</button>
|
||||||
|
<button class="btn btn-danger btn-sm btn-delete" type="button" data-id="{{ $p->id }}" data-nama="{{ $p->nama }}">Hapus</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr><td colspan="8" class="text-center">Belum ada data petugas.</td></tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MODAL FORM -->
|
||||||
|
<div class="modal fade" id="modal-petugas" tabindex="-1" data-bs-backdrop="static">
|
||||||
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="modal-title">Tambah Petugas</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<form id="form-petugas" enctype="multipart/form-data">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="_method" id="form-method" value="POST">
|
||||||
|
<input type="hidden" id="form-id" value="">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-md-3 text-center">
|
||||||
|
<img class="foto-preview mb-2" id="foto-preview" src="{{ asset('images/no-image.jpg') }}" alt="preview">
|
||||||
|
<input type="file" class="form-control form-control-sm" name="foto" id="foto" accept="image/*">
|
||||||
|
<small class="text-muted">JPG/PNG/BMP, maks 2MB</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-md-6"><label class="form-label">Nomer *</label><input class="form-control" name="nomer" id="f-nomer" maxlength="50" required></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Nama *</label><input class="form-control" name="nama" id="f-nama" maxlength="50" required></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Username *</label><input class="form-control" name="username" id="f-username" maxlength="50" required></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Password <span id="pw-hint">*</span></label><input type="text" class="form-control" name="password" id="f-password" maxlength="50" placeholder="Kosongkan jika tidak diubah"></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Level</label>
|
||||||
|
<select class="form-select" name="level_pegawai" id="f-level">
|
||||||
|
<option value="">-- Pilih --</option>
|
||||||
|
@foreach ($levels as $lv)
|
||||||
|
<option value="{{ $lv->level_code }}">{{ $lv->level_name }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6"><label class="form-label">NIP</label><input class="form-control" name="nip" id="f-nip" maxlength="50"></div>
|
||||||
|
<div class="col-md-4"><label class="form-label">Bisa Login</label>
|
||||||
|
<select class="form-select" name="bisalogin" id="f-bisalogin"><option value="1">Ya</option><option value="0">Tidak</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4"><label class="form-label">Status</label>
|
||||||
|
<select class="form-select" name="status" id="f-status"><option value="1">Aktif</option><option value="0">Nonaktif</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4"><label class="form-label">Jenis Kelamin</label>
|
||||||
|
<select class="form-select" name="jeniskelamin" id="f-jk"><option value="0">Laki-laki</option><option value="1">Perempuan</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Agama</label><input class="form-control" name="agama" id="f-agama" maxlength="50"></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Telepon</label><input class="form-control" name="telepon" id="f-telepon" maxlength="255"></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Tgl Lahir</label><input type="date" class="form-control" name="tgl_lahir" id="f-tgl-lahir"></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Tgl Masuk</label><input type="date" class="form-control" name="tgl_masuk" id="f-tgl-masuk"></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Email</label><input class="form-control" name="email" id="f-email" maxlength="64"></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Alamat</label><input class="form-control" name="alamat" id="f-alamat" maxlength="100"></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Alamat Lahir</label><input class="form-control" name="alamat_lahir" id="f-alamat-lahir" maxlength="255"></div>
|
||||||
|
<div class="col-md-6"><label class="form-label">Alamat Sekarang</label><input class="form-control" name="alamat_sekarang" id="f-alamat-sekarang" maxlength="255"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="submit" class="btn btn-primary">Simpan</button>
|
||||||
|
<button type="button" class="btn btn-danger" data-bs-dismiss="modal">Batal</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MODAL DETAIL -->
|
||||||
|
<div class="modal fade" id="modal-detail" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header"><h5 class="modal-title">Detail Petugas</h5><button class="btn-close" data-bs-dismiss="modal"></button></div>
|
||||||
|
<div class="modal-body" id="detail-body"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('javascript')
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
|
||||||
|
<script src="{{ asset('vendor/sweetalert2/dist/sweetalert2.min.js') }}"></script>
|
||||||
|
<script>
|
||||||
|
$(function () {
|
||||||
|
const csrf = $('meta[name="csrf-token"]').attr('content');
|
||||||
|
const noImage = '{{ asset('images/no-image.jpg') }}';
|
||||||
|
const levelMap = @json($levels->pluck('level_name', 'level_code'));
|
||||||
|
const routes = {
|
||||||
|
store: '{{ route('petugas.store', [], false) }}',
|
||||||
|
show: '{{ route('petugas.show', ':id', false) }}',
|
||||||
|
update: '{{ route('petugas.update', ':id', false) }}',
|
||||||
|
destroy: '{{ route('petugas.destroy', ':id', false) }}',
|
||||||
|
foto: '{{ route('petugas.foto', ':id', false) }}'
|
||||||
|
};
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('modal-petugas'));
|
||||||
|
|
||||||
|
function errMsg(xhr) {
|
||||||
|
if (xhr.status === 419) return 'Sesi berakhir, silakan muat ulang halaman.';
|
||||||
|
if (xhr.status === 422 && xhr.responseJSON?.errors) {
|
||||||
|
return Object.values(xhr.responseJSON.errors).flat().join('\n');
|
||||||
|
}
|
||||||
|
return xhr.responseJSON?.message || 'Operasi gagal.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#foto').on('change', function () {
|
||||||
|
const f = this.files[0];
|
||||||
|
$('#foto-preview').attr('src', f ? URL.createObjectURL(f) : noImage);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btnAdd').on('click', function () {
|
||||||
|
$('#form-petugas')[0].reset();
|
||||||
|
$('#form-id').val('');
|
||||||
|
$('#form-method').val('POST');
|
||||||
|
$('#pw-hint').text('*');
|
||||||
|
$('#f-password').prop('required', true);
|
||||||
|
$('#foto-preview').attr('src', noImage);
|
||||||
|
$('#modal-title').text('Tambah Petugas');
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.btn-edit', function () {
|
||||||
|
const id = $(this).data('id');
|
||||||
|
const btn = $(this); const btnHtml = btn.html();
|
||||||
|
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm"></span>');
|
||||||
|
$.get(routes.show.replace(':id', id), function (r) {
|
||||||
|
const d = r.data;
|
||||||
|
$('#form-id').val(d.id);
|
||||||
|
$('#form-method').val('PUT');
|
||||||
|
$('#pw-hint').text('(kosongkan jika tidak diubah)');
|
||||||
|
$('#f-password').prop('required', false).val('');
|
||||||
|
$('#f-nomer').val(d.nomer);
|
||||||
|
$('#f-nama').val(d.nama);
|
||||||
|
$('#f-username').val(d.username);
|
||||||
|
$('#f-level').val(d.level_pegawai);
|
||||||
|
$('#f-nip').val(d.nip);
|
||||||
|
$('#f-bisalogin').val(d.bisalogin ?? 1);
|
||||||
|
$('#f-status').val(d.status ?? 1);
|
||||||
|
$('#f-jk').val(d.jeniskelamin ?? 0);
|
||||||
|
$('#f-agama').val(d.agama);
|
||||||
|
$('#f-telepon').val(d.telepon);
|
||||||
|
$('#f-tgl-lahir').val(d.tgl_lahir);
|
||||||
|
$('#f-tgl-masuk').val(d.tgl_masuk);
|
||||||
|
$('#f-email').val(d.email);
|
||||||
|
$('#f-alamat').val(d.alamat);
|
||||||
|
$('#f-alamat-lahir').val(d.alamat_lahir);
|
||||||
|
$('#f-alamat-sekarang').val(d.alamat_sekarang);
|
||||||
|
$('#foto').val('');
|
||||||
|
$('#foto-preview').attr('src', r.has_foto ? routes.foto.replace(':id', d.id) + '?t=' + Date.now() : noImage);
|
||||||
|
$('#modal-title').text('Ubah Petugas');
|
||||||
|
modal.show();
|
||||||
|
}).fail(xhr => Swal.fire({icon: 'error', title: 'Gagal memuat data', text: errMsg(xhr)})).always(() => btn.prop('disabled', false).html(btnHtml));
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#form-petugas').on('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const id = $('#form-id').val();
|
||||||
|
const isEdit = !!id;
|
||||||
|
const url = isEdit ? routes.update.replace(':id', encodeURIComponent(id)) : routes.store;
|
||||||
|
const fd = new FormData(this);
|
||||||
|
if (isEdit) fd.set('_method', 'PUT');
|
||||||
|
const saveBtn = $(this).find('[type="submit"]'); const saveHtml = saveBtn.html();
|
||||||
|
saveBtn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Menyimpan...');
|
||||||
|
$.ajax({
|
||||||
|
url: url, type: 'POST', data: fd, processData: false, contentType: false,
|
||||||
|
headers: {'X-CSRF-TOKEN': csrf},
|
||||||
|
success: () => window.location.reload(),
|
||||||
|
error: xhr => {
|
||||||
|
saveBtn.prop('disabled', false).html(saveHtml);
|
||||||
|
Swal.fire({icon: 'error', title: 'Gagal menyimpan', text: errMsg(xhr)});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.btn-delete', function () {
|
||||||
|
const id = $(this).data('id');
|
||||||
|
const nama = $(this).data('nama');
|
||||||
|
Swal.fire({title: 'Hapus petugas?', text: nama, icon: 'warning', showCancelButton: true, confirmButtonText: 'Ya, hapus', cancelButtonText: 'Batal', confirmButtonColor: '#dc3545', showLoaderOnConfirm: true, preConfirm: () => $.ajax({url: routes.destroy.replace(':id', encodeURIComponent(id)), type: 'DELETE', headers: {'X-CSRF-TOKEN': csrf}}).catch(xhr => { Swal.showValidationMessage(errMsg(xhr)); })})
|
||||||
|
.then(res => {
|
||||||
|
if (res.isConfirmed && res.value) {
|
||||||
|
Swal.fire({icon: 'success', title: 'Berhasil', text: 'Petugas dihapus.', timer: 1500, showConfirmButton: false}).then(() => window.location.reload());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.btn-detail', function () {
|
||||||
|
const id = $(this).data('id');
|
||||||
|
const btn = $(this); const btnHtml = btn.html();
|
||||||
|
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm"></span>');
|
||||||
|
$('#detail-body').html('<div class="text-center py-3"><span class="spinner-border spinner-border-sm me-2"></span>Memuat...</div>');
|
||||||
|
new bootstrap.Modal(document.getElementById('modal-detail')).show();
|
||||||
|
$.get(routes.show.replace(':id', id), function (r) {
|
||||||
|
const d = r.data;
|
||||||
|
const img = r.has_foto ? routes.foto.replace(':id', d.id) : noImage;
|
||||||
|
$('#detail-body').html(
|
||||||
|
'<div class="text-center mb-3"><img src="' + img + '" class="foto-preview" alt="foto"></div>' +
|
||||||
|
'<dl class="row mb-0">' +
|
||||||
|
'<dt class="col-4">Nomer</dt><dd class="col-8">' + (d.nomer ?? '-') + '</dd>' +
|
||||||
|
'<dt class="col-4">Nama</dt><dd class="col-8">' + (d.nama ?? '-') + '</dd>' +
|
||||||
|
'<dt class="col-4">Username</dt><dd class="col-8">' + (d.username ?? '-') + '</dd>' +
|
||||||
|
'<dt class="col-4">Level</dt><dd class="col-8">' + (levelMap[d.level_pegawai] ?? d.level_pegawai ?? '-') + '</dd>' +
|
||||||
|
'<dt class="col-4">NIP</dt><dd class="col-8">' + (d.nip ?? '-') + '</dd>' +
|
||||||
|
'<dt class="col-4">Telepon</dt><dd class="col-8">' + (d.telepon ?? '-') + '</dd>' +
|
||||||
|
'<dt class="col-4">Alamat</dt><dd class="col-8">' + (d.alamat ?? '-') + '</dd>' +
|
||||||
|
'<dt class="col-4">Tgl Lahir</dt><dd class="col-8">' + (d.tgl_lahir ?? '-') + '</dd>' +
|
||||||
|
'<dt class="col-4">Tgl Masuk</dt><dd class="col-8">' + (d.tgl_masuk ?? '-') + '</dd>' +
|
||||||
|
'</dl>'
|
||||||
|
);
|
||||||
|
}).fail(xhr => $('#detail-body').html('<div class="text-danger text-center">' + errMsg(xhr) + '</div>')).always(() => btn.prop('disabled', false).html(btnHtml));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endsection
|
||||||
@@ -173,11 +173,21 @@ function formRow(mode, vehicle) {
|
|||||||
|
|
||||||
$(document).on('submit', '.vehicle-inline-form', function (event) {
|
$(document).on('submit', '.vehicle-inline-form', function (event) {
|
||||||
event.preventDefault(); const form = $(this); const id = form.data('id'); const edit = form.data('mode') === 'edit';
|
event.preventDefault(); const form = $(this); const id = form.data('id'); const edit = form.data('mode') === 'edit';
|
||||||
$.ajax({ url: edit ? routes.update.replace(':id', encodeURIComponent(id)) : routes.store, type: edit ? 'PUT' : 'POST', data: form.serialize(), headers: {'X-CSRF-TOKEN': csrf}, success: () => window.location.reload(), error: xhr => alert(xhr.responseJSON?.message || Object.values(xhr.responseJSON?.errors || {}).flat().join('\n') || 'Gagal menyimpan kendaraan.') });
|
const btn = form.find('[type="submit"]'); const btnHtml = btn.html();
|
||||||
|
btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Menyimpan...');
|
||||||
|
$.ajax({
|
||||||
|
url: edit ? routes.update.replace(':id', encodeURIComponent(id)) : routes.store,
|
||||||
|
type: edit ? 'PUT' : 'POST', data: form.serialize(), headers: {'X-CSRF-TOKEN': csrf},
|
||||||
|
success: () => window.location.reload(),
|
||||||
|
error: xhr => {
|
||||||
|
btn.prop('disabled', false).html(btnHtml);
|
||||||
|
Swal.fire({icon: 'error', title: 'Gagal menyimpan', text: xhr.responseJSON?.message || Object.values(xhr.responseJSON?.errors || {}).flat().join('\n') || 'Gagal menyimpan kendaraan.'});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btnDeleteVehicle').on('click', function () { if (!selected) return; Swal.fire({title:'Hapus kendaraan?', text:`${selected.vehicleName} beserta tarif terkait akan dihapus.`, icon:'warning', showCancelButton:true, confirmButtonText:'Ya, hapus', cancelButtonText:'Batal', confirmButtonColor:'#dc3545'}).then(result => { if (!result.isConfirmed) return; $.ajax({url: routes.destroy.replace(':id', encodeURIComponent(selected.vehicleId)), type:'DELETE', headers:{'X-CSRF-TOKEN':csrf}, success:() => window.location.reload(), error:xhr => Swal.fire({icon:'error', title:'Gagal menghapus', text:xhr.responseJSON?.message || 'Kendaraan tidak dapat dihapus.'})}); }); });
|
$('#btnDeleteVehicle').on('click', function () { if (!selected) return; Swal.fire({title:'Hapus kendaraan?', text:`${selected.vehicleName} beserta tarif terkait akan dihapus.`, icon:'warning', showCancelButton:true, confirmButtonText:'Ya, hapus', cancelButtonText:'Batal', confirmButtonColor:'#dc3545', showLoaderOnConfirm: true, preConfirm: () => $.ajax({url: routes.destroy.replace(':id', encodeURIComponent(selected.vehicleId)), type:'DELETE', headers:{'X-CSRF-TOKEN':csrf}}).catch(xhr => { Swal.showValidationMessage(xhr.responseJSON?.message || 'Kendaraan tidak dapat dihapus.'); })}).then(result => { if (result.isConfirmed && result.value) { Swal.fire({icon: 'success', title: 'Berhasil', text: 'Kendaraan dihapus.', timer: 1500, showConfirmButton: false}).then(() => window.location.reload()); } }); });
|
||||||
$('#btnDetail').on('click', function () { if (!selected) return; $('#vehicle-detail-body').html('<div class="text-center py-3">Memuat detail...</div>'); new bootstrap.Modal(document.getElementById('vehicle-detail-modal')).show(); $.get(routes.show.replace(':id', encodeURIComponent(selected.vehicleId)), function(r) { const t = r.tarif; $('#vehicle-detail-body').html(`<dl class="row mb-0"><dt class="col-5">ID Kendaraan</dt><dd class="col-7">${r.data.id}</dd><dt class="col-5">Nama</dt><dd class="col-7">${r.data.nama}</dd><dt class="col-5">Shortcut</dt><dd class="col-7">${r.data.short_cut}</dd><dt class="col-5">Tarif Awal</dt><dd class="col-7">${t ? Number(t.tarif || 0).toLocaleString('id-ID') : '-'}</dd><dt class="col-5">Tarif Interval</dt><dd class="col-7">${t ? Number(t.tarif_interval || 0).toLocaleString('id-ID') : '-'}</dd><dt class="col-5">Waktu Gratis</dt><dd class="col-7">${t?.waktu_gratis ?? '-' } menit</dd></dl>`); }); });
|
$('#btnDetail').on('click', function () { if (!selected) return; const btn = $(this); const btnHtml = btn.html(); btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Memuat...'); $('#vehicle-detail-body').html('<div class="text-center py-3"><span class="spinner-border spinner-border-sm me-2"></span>Memuat detail...</div>'); new bootstrap.Modal(document.getElementById('vehicle-detail-modal')).show(); $.get(routes.show.replace(':id', encodeURIComponent(selected.vehicleId)), function(r) { const t = r.tarif; $('#vehicle-detail-body').html(`<dl class="row mb-0"><dt class="col-5">ID Kendaraan</dt><dd class="col-7">${r.data.id}</dd><dt class="col-5">Nama</dt><dd class="col-7">${r.data.nama}</dd><dt class="col-5">Shortcut</dt><dd class="col-7">${r.data.short_cut}</dd><dt class="col-5">Tarif Awal</dt><dd class="col-7">${t ? Number(t.tarif || 0).toLocaleString('id-ID') : '-'}</dd><dt class="col-5">Tarif Interval</dt><dd class="col-7">${t ? Number(t.tarif_interval || 0).toLocaleString('id-ID') : '-'}</dd><dt class="col-5">Waktu Gratis</dt><dd class="col-7">${t?.waktu_gratis ?? '-' } menit</dd></dl>`); }).fail(xhr => { $('#vehicle-detail-body').html('<div class="text-danger text-center">' + (xhr.responseJSON?.message || 'Gagal memuat detail.') + '</div>'); }).always(() => btn.prop('disabled', false).html(btnHtml)); });
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<label class="col-lg-3 col-form-label form-label">Telepon</label>
|
<label class="col-lg-3 col-form-label form-label">Telepon</label>
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
<input type="number" class="form-control form-control-sm" phone="phone" value="{{ old('phone')}}">
|
<input type="text" inputmode="tel" class="form-control form-control-sm" name="phone" value="{{ old('phone')}}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
|
|||||||
@@ -39,6 +39,33 @@
|
|||||||
.table-selectable tbody tr.selected td {
|
.table-selectable tbody tr.selected td {
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tegaskan garis input di modal member (tambah/ubah/extend/detail).
|
||||||
|
Cuma warna border (tetap 1px), layout tidak berubah. */
|
||||||
|
#modal-member .form-control,
|
||||||
|
#modal-member .form-select,
|
||||||
|
#modal-extend .form-control,
|
||||||
|
#modal-extend .form-select,
|
||||||
|
#ubah-member .form-control,
|
||||||
|
#ubah-member .form-select,
|
||||||
|
#modal-details .form-control {
|
||||||
|
border-color: #9aa5b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Template global bikin border .table-bordered 5px — balikin ke 1px
|
||||||
|
khusus tabel di modal member. */
|
||||||
|
#modal-member .table-bordered > :not(caption) > *,
|
||||||
|
#modal-extend .table-bordered > :not(caption) > *,
|
||||||
|
#ubah-member .table-bordered > :not(caption) > *,
|
||||||
|
#modal-details .table-bordered > :not(caption) > * {
|
||||||
|
border-width: 1px 0;
|
||||||
|
}
|
||||||
|
#modal-member .table-bordered > :not(caption) > * > *,
|
||||||
|
#modal-extend .table-bordered > :not(caption) > * > *,
|
||||||
|
#ubah-member .table-bordered > :not(caption) > * > *,
|
||||||
|
#modal-details .table-bordered > :not(caption) > * > * {
|
||||||
|
border-width: 0 1px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<label class="col-lg-3 col-form-label form-label">Telepon</label>
|
<label class="col-lg-3 col-form-label form-label">Telepon</label>
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
<input type="text" class="form-control form-control-sm" phone="phone" placeholder="+62 81/081...">
|
<input type="text" class="form-control form-control-sm" name="phone" placeholder="+62 81/081...">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
<label class="col-lg-3 col-form-label form-label">Telepon</label>
|
<label class="col-lg-3 col-form-label form-label">Telepon</label>
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
<input type="number" class="form-control" phone="phone" placeholder="+62 81/081...">
|
<input type="text" inputmode="tel" class="form-control" name="phone" placeholder="+62 81/081...">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 row">
|
<div class="mb-3 row">
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
use App\Http\Controllers\Parking\Setup\Gate\MasterGateParameterController;
|
use App\Http\Controllers\Parking\Setup\Gate\MasterGateParameterController;
|
||||||
use App\Http\Controllers\Parking\Setup\Vehicle\MasterVehicleController;
|
use App\Http\Controllers\Parking\Setup\Vehicle\MasterVehicleController;
|
||||||
use App\Http\Controllers\Parking\Setup\Vehicle\ModifiedVehiclepriceController;
|
use App\Http\Controllers\Parking\Setup\Vehicle\ModifiedVehiclepriceController;
|
||||||
|
use App\Http\Controllers\Parking\Setup\Petugas\MasterPetugasController;
|
||||||
use App\Http\Controllers\Parking\Setup\UserLevel\LevelUserController;
|
use App\Http\Controllers\Parking\Setup\UserLevel\LevelUserController;
|
||||||
use App\Http\Controllers\Parking\Setup\UserLevel\LevelMenuController;
|
use App\Http\Controllers\Parking\Setup\UserLevel\LevelMenuController;
|
||||||
use App\Http\Controllers\Parking\Setup\Member\StasiunKerjaController;
|
use App\Http\Controllers\Parking\Setup\Member\StasiunKerjaController;
|
||||||
@@ -81,6 +82,8 @@
|
|||||||
Route::get("/dhcp/logs", [MasterConfigNetworkController::class, "logs"])->name("dhcp.logs");
|
Route::get("/dhcp/logs", [MasterConfigNetworkController::class, "logs"])->name("dhcp.logs");
|
||||||
Route::resource("vehicle", MasterVehicleController::class);
|
Route::resource("vehicle", MasterVehicleController::class);
|
||||||
Route::resource("price", ModifiedVehiclepriceController::class)->names("vehiclePrice");
|
Route::resource("price", ModifiedVehiclepriceController::class)->names("vehiclePrice");
|
||||||
|
Route::get("petugas/{petugas}/foto", [MasterPetugasController::class, "foto"])->name("petugas.foto");
|
||||||
|
Route::resource("petugas", MasterPetugasController::class)->names("petugas");
|
||||||
Route::resource("level-user", LevelUserController::class)->names("levelUser");
|
Route::resource("level-user", LevelUserController::class)->names("levelUser");
|
||||||
Route::resource("level-menu", LevelMenuController::class)->names("levelMenu");
|
Route::resource("level-menu", LevelMenuController::class)->names("levelMenu");
|
||||||
Route::get("stasiunkerja/data", [StasiunKerjaController::class, "data"])->name("stasiunkerja.data");
|
Route::get("stasiunkerja/data", [StasiunKerjaController::class, "data"])->name("stasiunkerja.data");
|
||||||
|
|||||||
Reference in New Issue
Block a user