Rapihin setup/member/lot-parking + fitur intercom, exclude WIP controller & file sampah dari git

Kumpulan perubahan yang udah numpuk di working tree: refactor Setup
(Gate/Vehicle/Member), rapihin memberin & stiker, service parking baru
(ParkingUtilsServices, NetworkService), model/migration lot-parking &
DHCP reservation, dan fitur baru Intercom (controller/model/migration/
view).

ReportTransaksiController versi lokal ini WIP (~90%, masih didebug) -
versi yang bener ada di server, jadi dikeluarin dari git (ditambahin
ke .gitignore) biar gak numpuk nindihin punya server pas git pull di
sana. Sekalian keluarin dump mentah tabel softseting (1.8MB, nyasar
ke-commit), file .bak, dan arsip ad-hoc (api.tgz, c.zip) yang emang
bukan source code.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EAJc2mrpj2cSTe4oowLPzQ
This commit is contained in:
2026-10-04 07:15:54 +07:00
co-authored by Claude Sonnet 5
parent efdea9d739
commit 2a9b28581c
91 changed files with 5716 additions and 2470 deletions
@@ -1,8 +1,7 @@
@extends('layouts.master')
@section('styles')
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
<style>
.card-body label {
font-size: 13px;
@@ -53,49 +52,49 @@
<div class="main-panel">
<div class="content-wrapper">
<div class="row">
<div class="col-2">
</div>
<div class="col-12 col-md-8 grid-margin justify-content-center stretch-card">
<div class="card">
<div class="card-body">
<h4 class="card-title">Daftar Pintu/Pos</h4>
<div class="table-responsive mb-2">
<table class="table table-bordered table-selectable" id="example" data-url="{{ route('getMember') }}">
<thead>
<tr>
<th class="p-3" width="3%">No.</th>
<th style="text-align: center;" width="10%">Level Code</th>
<th style="text-align: center;">Level Pengguna</th>
<th style="text-align: center;">Tanggal Dibuat</th>
<th style="text-align: center;">Hak Akses</th>
</tr>
</thead>
<tbody>
@foreach ($levelAvail as $level)
<div class="col-12 col-md-8 grid-margin stretch-card">
<div class="card shadow-sm h-100">
<div class="card">
<div class="card-body">
<h4 class="card-title">Daftar Pintu/Pos</h4>
<div class="table-responsive mb-2">
<table class="table table-bordered table-selectable" id="example" data-url="">
<thead>
<tr>
<td class="text-center">{{ $loop->iteration }}</td>
<td class="text-center" hidden>{{ $level->level_id }}</td>
<td class="text-center" width="7%">{{ $level->level_code }} </td>
<td>{{ $level->level_name }}</td>
<td class="text-center" width="10%">{{ \Carbon\Carbon::parse($level->create_date)->format('Y-m-d')}}</td>
<td class="text-center" width="12%">
<button type="button" class="user-acess" data-bs-toggle="modal" title="Izin Akses" data-bs-target=".bd-ubah-modal-lg"><i class="fa-solid fa-user-gear"></i></button>
<button type="button" class="user-menu" data-bs-toggle="modal" id="menuakses" title="Menu Akses" data-bs-target=".user-menu-modal-lg"><i class="fa-solid fa-bars"></i></button>
</td>
<th class="p-3" width="3%">No.</th>
<th style="text-align: center;" width="10%">Level Code</th>
<th style="text-align: center;">Level Pengguna</th>
<th style="text-align: center;">Tanggal Dibuat</th>
<th style="text-align: center;">Hak Akses</th>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="d-flex justify-content-center align-items-center mt-2">
<div>
<button class="btn btn-primary btn-sm add-button" data-bs-toggle="modal" data-bs-target=".bd-example-modal-lg">Tambah</button>
<button class="btn btn-warning btn-sm edit-button" id="btnEdit" data-bs-toggle="modal" data-bs-target=".bd-ubah-modal-lg">Ubah</button>
<button class="btn btn-danger btn-sm" id="btnDelete">Hapus</button>
<button class="btn btn-danger btn-sm" id="btnDelete">Detail</button>
</thead>
<tbody>
@foreach ($levelAvail as $level)
<tr>
<td class="text-center">{{ $loop->iteration }}</td>
<td class="text-center" hidden>{{ $level->level_id }}</td>
<td class="text-center" width="7%">{{ $level->level_code }} </td>
<td>{{ $level->level_name }}</td>
<td class="text-center" width="10%">{{ \Carbon\Carbon::parse($level->create_date)->format('Y-m-d')}}</td>
<td class="text-center" width="12%">
<button type="button" class="user-acess" data-bs-toggle="modal" title="Izin Akses" data-bs-target=".bd-ubah-modal-lg"><i class="fa-solid fa-user-gear"></i></button>
<button type="button" class="user-menu" data-bs-toggle="modal" id="menuakses" title="Menu Akses" data-bs-target=".user-menu-modal-lg"><i class="fa-solid fa-bars"></i></button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="d-flex justify-content-center align-items-center mt-2">
<div>
<button class="btn btn-primary btn-sm add-button" data-bs-toggle="modal" data-bs-target=".bd-example-modal-lg">Tambah</button>
<button class="btn btn-warning btn-sm edit-button" id="btnEdit" data-bs-toggle="modal" data-bs-target=".bd-ubah-modal-lg">Ubah</button>
<button class="btn btn-danger btn-sm" id="btnDelete">Hapus</button>
<button class="btn btn-danger btn-sm" id="btnDelete">Detail</button>
</div>
</div>
</div>
</div>
<!--</div>-->
</div>
<div class="col-2">
<!-- ADD MEMMBER MODAL -->
+67 -33
View File
@@ -162,19 +162,25 @@
let id = $(this).data('id');
let nama = $(this).data('nama');
// # base url tanpa parameter biar tidak dobel slash
let baseUrl = "{{ url('setup/master/gate-hardware') }}";
$.ajax({
url: "{{ route('hardware.show', '') }}/" + id,
url: baseUrl + "/" + id,
type: 'GET',
success: function (r) {
r = r || {};
console.log(r);
let colSpan = row.children('td').length;
// Field yang mau ditampilkan (pilih sendiri mau yang mana)
// # daftar field yang ditampilkan (key database + label)
let fields = [
['printer1_avail', 'Printer Status'],
['printer1_conn', 'Printer Connection'],
['printer1_spd', 'Printer Speed/Baudrate'],
['printer1_dummy', 'Jarak Potong Kertas'],
['video1_avail', 'Kamera Status'],
['video1_conn', 'Kamera URL/Path'],
['sound_avail', 'Sound Status'],
@@ -183,25 +189,32 @@
['bank_reader_avail', 'Reader BANK Status'],
['bank_reader_comm', 'Reader BANK Connection'],
['bank_reader_key', 'Reader BANK Key/Parameter'],
['smartcard_avail', 'Smartcard Status'],
['smartcard_speed', 'Smartcard Speed/Baudrate'],
['smartcard_port', 'Smartcard Port'],
];
let rows = '';
let cards = '';
for (let i = 0; i < fields.length; i ++) {
let f1 = fields[i];
let val = r[f1[0]] ?? '';
rows += `
<tr>
<td class="fw-semibold text-muted text-label"><strong>${f1[1]}</strong></td>
<td>
<span class="view-mode">${formatValue(f1[0], r[f1[0]])}</span>
<input type="text"
class="form-control form-control-sm edit-mode d-none"
data-key="${f1[0]}"
value="${r[f1[0]] ?? ''}">
</td>
</tr>
cards += `
<div class="col-12 col-sm-6 col-lg-3 mb-2">
<div class="border rounded p-2 h-100">
<div class="fw-semibold text-muted text-label mb-1"><strong>${f1[1]}</strong></div>
<div>
<span class="view-mode">${formatValue(f1[0], r[f1[0]])}</span>
<input type="text"
class="form-control form-control-sm edit-mode d-none"
data-key="${f1[0]}"
data-orig="${esc(val)}"
value="${esc(val)}">
</div>
</div>
</div>
`;
}
@@ -210,7 +223,7 @@ class="form-control form-control-sm edit-mode d-none"
<td colspan="${colSpan}">
<div class="card shadow-sm">
<div class="card-header d-flex justify-content-between align-items-center">
<strong>Hardware Configuration</strong>
<strong>Hardware Configuration - ${esc(nama)} (${esc(id)})</strong>
<div>
<button class="btn btn-sm btn-primary btn-edit">Edit</button>
<button class="btn btn-sm btn-success btn-save d-none">Save</button>
@@ -218,17 +231,9 @@ class="form-control form-control-sm edit-mode d-none"
</div>
</div>
<div class="card-body p-2">
<table class="table table-bordered table-sm align-middle mb-0">
<thead class="table-light">
<tr>
<th width="15%">Setting</th>
<th width="85%">Value</th>
</tr>
</thead>
<tbody>
${rows}
</tbody>
</table>
<div class="row">
${cards}
</div>
</div>
</div>
</td>
@@ -256,6 +261,11 @@ class="form-control form-control-sm edit-mode d-none"
// CANCEL
$(document).on('click', '.btn-cancel', function () {
// # kembalikan isian ke nilai semula
$('.edit-mode').each(function () {
$(this).val($(this).data('orig') ?? '');
});
$('.edit-mode').addClass('d-none');
$('.view-mode').removeClass('d-none');
@@ -267,6 +277,7 @@ class="form-control form-control-sm edit-mode d-none"
$(document).on('click', '.btn-save', function () {
let dataToSend = {};
let gateId = $('.edit-row').data('id');
$('.edit-mode').each(function () {
let key = $(this).data('key');
@@ -274,28 +285,48 @@ class="form-control form-control-sm edit-mode d-none"
});
console.log(dataToSend);
let gateId = $('.edit-row').data('id');
// # base url tanpa parameter biar tidak dobel slash
let baseUrl = "{{ url('setup/master/gate-hardware') }}";
$.ajax({
url: "{{ route('hardware.update', '') }}/" + gateId,
type: "PUT",
url: baseUrl + "/" + gateId,
type: "POST",
data: {
_token: "{{ csrf_token() }}",
_method: "PUT",
data: dataToSend
},
success: function () {
success: function (res) {
// # tampilkan ulang nilai yang baru disimpan
$('.edit-mode').each(function () {
let key = $(this).data('key');
let val = $(this).val();
$(this).siblings('.view-mode').text(val);
$(this).data('orig', val);
$(this).siblings('.view-mode').html(formatValue(key, val));
});
$('.btn-cancel').click();
alert('Berhasil disimpan 🔥');
alert('Berhasil disimpan');
},
error: function (xhr) {
console.error(xhr.responseText);
alert('Gagal menyimpan. Cek koneksi / hubungi admin.');
}
});
});
// # amankan teks biar tanda kutip tidak merusak HTML
function esc(value) {
if (value === null || value === undefined) return '';
return String(value)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
function formatValue(key, value) {
// kalau field *_avail kita kasih badge
@@ -309,7 +340,10 @@ function formatValue(key, value) {
}
// default
return value ?? '-';
if (value === null || value === undefined || value === '') {
return '-';
}
return esc(value);
}
});
+603
View File
@@ -0,0 +1,603 @@
@extends('layouts.master')
@section('title', 'DHCP Reservations')
@section('content')
<div class="container-fluid py-4">
{{-- Header --}}
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="mb-1">DHCP Reservations</h4>
<div class="text-muted small">Manage static DHCP leases by MAC address</div>
</div>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#reservationModal" onclick="openCreateModal()"><i class="bi bi-plus-lg me-1"></i> Add Reservation</button>
</div>
{{-- DHCP Server Card --}}
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<div class="row align-items-center">
{{-- Server --}}
<div class="col-md-5">
<div class="d-flex align-items-center">
<div class="rounded-circle bg-success bg-opacity-10 d-flex align-items-center justify-content-center me-3" style="width:48px;height:48px;">
<i class="bi bi-hdd-network text-success fs-4"></i>
</div>
<div>
<div class="fw-semibold" id="dhcpServerHostname">DHCP LAB</div>
<div class="text-muted small"> <span id="dhcpServerIp">-</span>
<span class="mx-1">•</span>
<span id="dhcpServerInterface">-</span></div>
</div>
</div>
</div>
{{-- Status --}}
<div class="col-md-3 mt-3 mt-md-0">
<div class="small text-muted">Status</div>
<div class="mt-1">
@if($dhcpStatus['running'])
<span id="dhcpStatusBadge" class="badge rounded-pill bg-success bg-opacity-10 text-success">
<span
class="d-inline-block rounded-circle bg-success me-1"
style="width:7px;height:7px;"
></span>
Running
</span>
@else
<span id="dhcpStatusBadge" class="badge rounded-pill bg-danger bg-opacity-10 text-danger">
<span
class="d-inline-block rounded-circle bg-danger me-1"
style="width:7px;height:7px;"
></span>
Stopped
</span>
@endif
</div>
</div>
{{-- Reservations --}}
<div class="col-md-2 mt-3 mt-md-0">
<div class="small text-muted">
Reservations
</div>
<div class="fw-semibold mt-1" id="activeClients">
{{ $dhcpStatus['active_clients'] }}
<span class="text-muted fw-normal small">
active
</span>
</div>
</div>
{{-- Last Apply --}}
<div class="col-md-2 mt-3 mt-md-0">
<div class="small text-muted">
Last Applied
</div>
<div
class="fw-semibold mt-1"
id="lastApplied"
>
-
</div>
</div>
</div>
</div>
</div>
{{-- Filters --}}
<div class="card border-0 shadow-sm mb-3">
<div class="card-body py-3">
<div class="row g-2">
<div class="col-md-5">
<div class="input-group">
<span class="input-group-text bg-white">
<i class="bi bi-search"></i>
</span>
<input
type="text"
class="form-control form-control-sm border-start-0"
id="searchReservation"
placeholder="Search hostname, MAC or IP..."
>
</div>
</div>
<div class="col-md-3">
<select
class="form-select"
id="statusFilter"
>
<option value="">All Status</option>
<option value="active">Active</option>
<option value="disabled">Disabled</option>
</select>
</div>
</div>
</div>
</div>
{{-- Reservation Table --}}
<div class="card border-0 shadow-sm">
<div class="card-body p-0">
<div class="table-responsive">
<table
class="table table-hover align-middle mb-0"
id="reservationTable">
<thead class="table-light">
<tr>
<th class="px-4">Hostname</th>
<th>MAC Address</th>
<th>IP Address</th>
<th>Status</th>
<th class="text-end px-4"></th>
</tr>
</thead>
<tbody>
@forelse($reservations as $reservation)
<tr>
<td class="px-4">
<div class="fw-semibold">
{{ $reservation->hostname }}
</div>
@if(!empty($reservation->description))
<div class="text-muted small">
{{ $reservation->description }}
</div>
@endif
</td>
<td>
<code>{{ $reservation->mac_address }}</code>
</td>
<td>
<span class="fw-semibold">
{{ $reservation->ip_address }}
</span>
</td>
<td>
@if($reservation->enabled)
<span class="badge rounded-pill bg-success bg-opacity-10 text-success">
Active
</span>
@else
<span class="badge rounded-pill bg-secondary bg-opacity-10 text-secondary">
Disabled
</span>
@endif
</td>
<td class="text-end px-4">
<div class="btn-group">
<button
type="button"
class="btn btn-sm btn-light"
onclick="editReservation(
@js($reservation->id),
@js($reservation->hostname),
@js($reservation->mac_address),
@js($reservation->ip_address),
@js((bool) $reservation->enabled)
)"
>
<i class="bi bi-pencil"></i>
</button>
<button
type="button"
class="btn btn-sm btn-light text-danger"
onclick="deleteReservation(
@js($reservation->id),
@js($reservation->hostname)
)"
>
<i class="bi bi-trash"></i>
</button>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center py-5 text-muted">
No DHCP reservations found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
{{-- Footer --}}
<div class="card-footer bg-white border-0">
<div class="d-flex justify-content-between align-items-center">
<div class="text-muted small">
Showing {{ $reservations->count() }} reservations
</div>
<div>
@if(method_exists($reservations, 'previousPageUrl') && $reservations->previousPageUrl())
<a href="{{ $reservations->previousPageUrl() }}"
class="btn btn-sm btn-outline-secondary">
Previous
</a>
@else
<button class="btn btn-sm btn-outline-secondary" disabled>
Previous
</button>
@endif
@if(method_exists($reservations, 'nextPageUrl') && $reservations->nextPageUrl())
<a href="{{ $reservations->nextPageUrl() }}"
class="btn btn-sm btn-outline-secondary">
Next
</a>
@else
<button class="btn btn-sm btn-outline-secondary" disabled>
Next
</button>
@endif
</div>
</div>
</div>
</div>
{{-- Apply Section --}}
<div class="card border-0 shadow-sm mt-4">
<div class="card-body">
<div class="row align-items-center">
<div class="col">
<div class="fw-semibold">
DHCP Configuration
</div>
<div class="text-muted small mt-1">
Changes are stored in the database
until you apply the configuration.
</div>
</div>
<div class="col-auto">
<button
class="btn btn-outline-secondary me-2"
onclick="testDhcp()"
>
<i class="bi bi-shield-check me-1"></i>
Test Configuration
</button>
<button
class="btn btn-primary"
onclick="applyDhcp()"
>
<i class="bi bi-arrow-repeat me-1"></i>
Apply Changes
</button>
</div>
</div>
</div>
</div>
<div class="card border-0 shadow-sm">
<div class="card-header bg-white border-0
d-flex justify-content-between
align-items-center">
<div>
<div class="fw-semibold">
Recent DHCP Events
</div>
<div class="text-muted small">
Latest activity from DHCP server
</div>
</div>
<button
type="button"
class="btn btn-sm btn-light"
onclick="loadDhcpLogs()"
>
<i class="bi bi-arrow-clockwise"></i>
Refresh
</button>
</div>
<div class="card-body p-0">
<div
id="dhcpLogs"
class="font-monospace small"
style="
max-height: 320px;
overflow-y: auto;
"
>
<div class="text-muted p-4">
Loading DHCP events...
</div>
</div>
</div>
</div>
</div>
{{-- Add / Edit Modal --}}
<div
class="modal fade"
id="reservationModal"
tabindex="-1"
>
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow">
<div class="modal-header">
<div>
<h5
class="modal-title mb-1"
id="reservationModalTitle"
>
Add DHCP Reservation
</h5>
<div class="text-muted small">
Assign a fixed IP address to a DHCP client
</div>
</div>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
></button>
</div>
<form id="reservationForm">
<div class="modal-body">
<input
type="hidden"
id="reservationId"
value=""
>
{{-- Hostname --}}
<div class="mb-3">
<label class="form-label fw-semibold">
Hostname
</label>
<input
type="text"
class="form-control"
id="hostname"
placeholder="e.g. kasir-01"
required
>
<div class="form-text">
Only letters, numbers, dash and underscore.
</div>
</div>
{{-- MAC --}}
<div class="mb-3">
<label class="form-label fw-semibold">
MAC Address
</label>
<input
type="text"
class="form-control font-monospace"
id="macAddress"
placeholder="AA:BB:CC:DD:EE:FF"
maxlength="17"
required
>
<div
class="invalid-feedback"
id="macError"
>
Invalid MAC address.
</div>
</div>
{{-- IP --}}
<div class="mb-3">
<label class="form-label fw-semibold">
IP Address
</label>
<input
type="text"
class="form-control font-monospace"
id="ipAddress"
placeholder="192.168.10.50"
required
>
<div
class="invalid-feedback"
id="ipError"
>
Invalid IP address.
</div>
</div>
{{-- Enabled --}}
<div
class="form-check form-switch"
>
<input
class="form-check-input"
type="checkbox"
id="enabled"
checked
>
<label
class="form-check-label"
for="enabled"
>
Enable reservation
</label>
</div>
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-light"
data-bs-dismiss="modal"
>
Cancel
</button>
<button
type="submit"
class="btn btn-primary"
>
<i class="bi bi-check-lg me-1"></i>
Save Reservation
</button>
</div>
</form>
</div>
</div>
</div>
{{-- Apply Confirmation --}}
<div
class="modal fade"
id="applyModal"
tabindex="-1"
>
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-0 shadow">
<div class="modal-body text-center p-4">
<div
class="rounded-circle bg-warning bg-opacity-10
d-flex align-items-center justify-content-center
mx-auto mb-3"
style="width:64px;height:64px;"
>
<i class="bi bi-exclamation-triangle text-warning fs-3"></i>
</div>
<h5>
Apply DHCP Configuration?
</h5>
<p class="text-muted mb-4">
The DHCP configuration will be validated
before the DHCP service is restarted.
</p>
<button
class="btn btn-light me-2"
data-bs-dismiss="modal"
>
Cancel
</button>
<button
class="btn btn-primary"
onclick="confirmApply()"
>
Apply Configuration
</button>
</div>
</div>
</div>
</div>
@endsection
@section('javascript')
<script>
window.dhcpConfig = {
reservationStoreUrl: @json(route('dhcp.reservations.store')),
reservationBaseUrl: @json(url('/dhcp/reservations')),
applyUrl: @json(route('dhcp.apply')),
statusUrl: @json(route('dhcp.status')),
logsUrl: @json(route('dhcp.logs')),
};
</script>
<script src="{{ asset('js/setup/dhcp.js') }}"></script>
@endsection
+54 -15
View File
@@ -56,11 +56,11 @@
<div class="card-body">
<h4 class="card-title">Master Kendaraan dan Tarif Parkir</h4>
<div class="table-responsive mb-2">
<table class="table table-bordered table-selectable" id="example" data-url="{{ route('getMember') }}">
<table class="table table-bordered table-selectable" id="vehicle-table">
<thead>
<tr>
<th class="p-3" width="5%">No.</th>
<th style="text-align: center;">Nama Pintu - <span class="fw-bold">(Shortcut)</span></th>
<th style="text-align: center;">Jenis Kendaraan - <span class="fw-bold">(Shortcut)</span></th>
{{-- <th style="text-align: center;">Mode Pintu</th> --}}
{{-- <th style="text-align: center;">Grace Periode</th> --}}
{{-- <th style="text-align: center;">Manless?</th> --}}
@@ -73,9 +73,7 @@
{{-- {{ dd($kendaraan) }} --}}
<td class="text-center">{{ $loop->iteration }}</td>
<label for="" type="hidden" value="{{ $kendaraan->id }}" ></label>
{{-- <td class="text-center" width="7%">{{ $pintu->id }}</td> --}}
<td>{{ $kendaraan->nama }} <strong> - ({{ $kendaraan->short_cut }})</strong></td>
<td data-vehicle-id="{{ $kendaraan->id }}" data-vehicle-name="{{ $kendaraan->nama }}" data-vehicle-shortcut="{{ $kendaraan->short_cut }}">{{ $kendaraan->nama }} <strong> - ({{ $kendaraan->short_cut }})</strong></td>
{{-- <td width="10%">
</td> --}}
{{-- <td class="text-center" width="7%"><span class="d-inline-block rounded-circle
@@ -88,7 +86,7 @@
</span>
</td> --}}
<td class="text-center" width="12%">
<a class="btn btn-warning btn-sm vehicle-price" id="vehicle-price" data-bs-toggle="modal" data-id="{{ $kendaraan->id}}" data-nama="{{ $kendaraan->nama}}" data-bs-target=".bd-price-modal-lg">Tarif</a>
<button class="btn btn-warning btn-sm vehicle-price" type="button" data-bs-toggle="modal" data-id="{{ $kendaraan->id}}" data-nama="{{ $kendaraan->nama}}" data-bs-target=".bd-price-modal-lg">Tarif</button>
{{-- <button class="btn btn-primary btn-sm hardware-button" data-id="{{ $pintu->id }}" data-nama="{{ $pintu->nama }}">Hardware</button> --}}
</td>
</tr>
@@ -96,14 +94,12 @@
</tbody>
</table>
</div>
<div class="d-flex justify-content-center align-items-center mt-2">
<div>
<button class="btn btn-primary btn-sm add-button" data-bs-toggle="modal" data-bs-target=".bd-example-modal-lg">Tambah</button>
{{-- </div>
<div id="rowActions" class="d-none"> --}}
<button class="btn btn-warning btn-sm edit-button" id="btnEdit" data-bs-toggle="modal" data-bs-target=".bd-ubah-modal-lg">Tambah</button>
<button class="btn btn-danger btn-sm" id="btnDelete">Hapus</button>
<button class="btn btn-info btn-sm" id="btnDetail">Detail</button>
<div class="d-flex justify-content-between align-items-center mt-3">
<button class="btn btn-primary btn-sm" id="btnAddVehicle" type="button"><i class="fa fa-plus me-1"></i>Tambah</button>
<div class="d-flex gap-2">
<button class="btn btn-info btn-sm" id="btnDetail" type="button" disabled><i class="fa fa-circle-info me-1"></i>Detail</button>
<button class="btn btn-warning btn-sm" id="btnEditVehicle" type="button" disabled><i class="fa fa-pen me-1"></i>Ubah</button>
<button class="btn btn-danger btn-sm" id="btnDeleteVehicle" type="button" disabled><i class="fa fa-trash me-1"></i>Hapus</button>
</div>
</div>
<!-- ADD MEMMBER MODAL -->
@@ -122,6 +118,12 @@
</div>
</div>
</div>
<div class="modal fade" id="vehicle-detail-modal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered"><div class="modal-content">
<div class="modal-header"><h5 class="modal-title">Detail Jenis Kendaraan</h5><button class="btn-close" data-bs-dismiss="modal"></button></div>
<div class="modal-body" id="vehicle-detail-body"></div>
</div></div>
</div>
</div>
@endsection
@@ -141,5 +143,42 @@
<script src="{{asset ('js/member/index.js') }}"></script>
{{-- <script src="{{asset ('js/setup/vehicle-price.js') }}"></script> --}}
<script src="{{asset ('js/setup/vehicle.js') }}"></script>
<script>
$(function () {
let selected = null;
const csrf = $('meta[name="csrf-token"]').attr('content');
const routes = {
store: '{{ route('vehicle.store', [], false) }}',
show: '{{ route('vehicle.show', ':id', false) }}',
update: '{{ route('vehicle.update', ':id', false) }}',
destroy: '{{ route('vehicle.destroy', ':id', false) }}'
};
function setSelected(row) {
$('#vehicle-table tbody tr').removeClass('table-active');
selected = row ? $(row).find('[data-vehicle-id]').data() : null;
if (row) $(row).addClass('table-active');
$('#btnDetail, #btnEditVehicle, #btnDeleteVehicle').prop('disabled', !selected);
}
function formRow(mode, vehicle) {
const isEdit = mode === 'edit';
return `<tr class="vehicle-form-row"><td colspan="3"><form class="vehicle-inline-form" data-mode="${mode}" data-id="${vehicle?.vehicleId || ''}"><div class="row g-2 align-items-end"><div class="col-md-2"><label class="form-label mb-1">ID</label><input class="form-control" name="id" maxlength="16" value="${vehicle?.vehicleId || ''}" ${isEdit ? 'readonly' : ''} required></div><div class="col-md-4"><label class="form-label mb-1">Nama Kendaraan</label><input class="form-control" name="nama" maxlength="100" value="${vehicle?.vehicleName || ''}" required></div><div class="col-md-2"><label class="form-label mb-1">Shortcut</label><input class="form-control text-uppercase" name="short_cut" maxlength="16" value="${vehicle?.vehicleShortcut || ''}" required></div>${isEdit ? '' : '<div class="col-md-2"><label class="form-label mb-1">Tarif Awal</label><input class="form-control" type="number" name="tarif" min="0" step="0.01" value="0" required></div>'}<div class="col-md-2 d-flex gap-2"><button class="btn btn-success" type="submit">Simpan</button><button class="btn btn-light border cancel-vehicle-form" type="button">Batal</button></div></div></form></td></tr>`;
}
$('#vehicle-table tbody').on('click', 'tr:not(.vehicle-form-row)', function () { setSelected(this); });
$('#btnAddVehicle').on('click', function () { $('.vehicle-form-row').remove(); $('#vehicle-table tbody').prepend(formRow('create')); setSelected(null); });
$('#btnEditVehicle').on('click', function () { if (!selected) return; $('.vehicle-form-row').remove(); $('#vehicle-table tbody tr').filter(function(){ return $(this).find('[data-vehicle-id]').data('vehicle-id') === selected.vehicleId; }).after(formRow('edit', selected)); });
$(document).on('click', '.cancel-vehicle-form', function () { $(this).closest('tr').remove(); });
$(document).on('submit', '.vehicle-inline-form', function (event) {
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.') });
});
$('#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.'})}); }); });
$('#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>`); }); });
});
</script>
@endsection
@endsection
@@ -22,7 +22,7 @@
<form class="tambah-member" id="show-tarif" action="">
@csrf
<div class="tab-content">
<div class="tab-pane fade show active" id="tarif1" role="tabpanel">
<div class="tab-pane fade show active" id="tarif" role="tabpanel">
<div class="pt-4">
<div class="row">
<div class="col-xl-12">
@@ -60,7 +60,7 @@
<option value="0">Pintu Keluar</option>
</select>
<label class="me-2 mb-0">Dengan nilai asuransi : </label>
<input type="number" class="form-control no-spinner" name="asuransi_incl" name="asuransi_incl" style="width:100px" value="0">
<input type="number" class="form-control no-spinner" id="asuransi_incl" name="asuransi_incl" style="width:100px" value="0">
</div>
<hr>
<h4 class="fw-bold">Jam Berikutnya</h4>
@@ -85,13 +85,13 @@
<label class="me-2 mb-0">Tarif Maksimum adalah </label>
<input type="number" class="form-control no-spinner" id="maksimum" name="maksimum" style="width:100px" value="1000">
<label class="me-2 ms-4 mb-0">Per</label>
<input type="number" class="form-control no-spinner-time" id="maks_cond" name="maks_cond" style="width:65px" value="24">
<select class="form-select me-2 ms-4 no-spinner" name="maksimum-cond" id="maksimum-cond" style="width:100px" >
<option value="0">Jam</option>
<option value="1">Hari</option>
<input type="number" class="form-control no-spinner-time" id="maksimum_hour" name="maksimum_hour" style="width:65px" value="24">
<select class="form-select me-2 ms-4 no-spinner" name="maksimum_mode" id="maksimum_mode" style="width:100px" >
<option value="hour">Jam</option>
<option value="day">Hari</option>
</select>
<label class="me-2 ms-4 mb-0 maks_cond_day">Minimum Menit : </label>
<input type="number" class="form-control no-spinner-time" id="maks_cond_day" name="maks_cond_day" style="width:75px" value="24">
<input type="number" class="form-control no-spinner-time" id="maksimum_day_minute" name="maksimum_day_minute" style="width:75px" value="24">
</div>
<hr>
<h4 class="fw-bold">Denda Tiket</h4>
@@ -104,10 +104,10 @@
</div>
<hr>
<div class="d-flex align-items-center">
<input class="form-check-input" type="checkbox">Denda Hanya Berlaku Bagi Kendaaran Casual/Umum </input>
<input class="form-check-input me-2" type="checkbox" id="denda_only_casual" name="denda_only_casual" value="1"><label for="denda_only_casual">Denda hanya berlaku bagi kendaraan casual/umum</label>
</div>
<div class="d-flex align-items-center mb-2">
<input class="form-check-input" type="checkbox">Denda Tidak Memperhitungkan Lama Parkir</input>
<input class="form-check-input me-2" type="checkbox" id="denda_only" name="denda_only" value="1"><label for="denda_only">Denda tidak memperhitungkan lama parkir</label>
</div>
</div>
{{-- <div class="col-xl-6">
@@ -241,9 +241,9 @@
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary simpan" data-url="{{ route('vehicle.update', ':id') }}">Save changes</button>
<button type="button" class="btn btn-primary simpan" data-url="{{ route('vehiclePrice.update', ':id', false) }}">Save changes</button>
<button type="button" class="btn btn-danger light" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>