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:
@@ -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 -->
|
||||
|
||||
@@ -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, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user