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
+235
View File
@@ -0,0 +1,235 @@
@extends('layouts.master')
@section('styles')
<link href="{{ asset('vendor/datatables/css/jquery.dataTables.min.css') }}" rel="stylesheet">
<style>
.dev-card { text-align: center; padding: 14px 8px; border-radius: 8px; }
.dev-id { font-size: 22px; font-weight: 800; }
.badge-status { font-size: 12px; padding: 4px 10px; border-radius: 20px; }
.st-idle { background: #e8f5e9; color: #2e7d32; }
.st-calling { background: #fff3e0; color: #ef6c00; animation: blink 1s infinite; }
.st-active { background: #e3f2fd; color: #1565c0; }
.st-busy { background: #fce4ec; color: #c62828; }
.st-offline { background: #eeeeee; color: #616161; }
@keyframes blink { 50% { opacity: .45; } }
.queue-card { border-left: 5px solid #fb8c00; }
.queue-card.active-session { border-left-color: #1e88e5; }
.timer { font-variant-numeric: tabular-nums; font-weight: 700; }
#talkHint kbd { background: #333; color: #fff; border-radius: 4px; padding: 1px 7px; }
</style>
@endsection
@section('content')
<div class="container-fluid page-body-wrapper">
<div class="main-panel">
<div class="content-wrapper">
<div class="row mb-2">
<div class="col-md-8">
<h4 class="font-weight-bold">Intercom Pusat <small class="text-muted">— local network only</small></h4>
<p class="text-muted mb-0">Client ketok via 1 tombol &rarr; masuk antrean &rarr; operator TERIMA &rarr; sesi private (client lain tidak dengar).</p>
</div>
<div class="col-md-4 text-end">
<span id="srvStatus" class="badge badge-secondary">menghubungkan…</span>
<button class="btn btn-sm btn-light border ml-2" onclick="loadAll()">⟳ Refresh</button>
</div>
</div>
<div class="row">
{{-- ANTREAN --}}
<div class="col-md-5 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h5>Antrean <span id="queueCount" class="badge badge-warning">0</span></h5>
<p class="text-muted small">Bunyi <i>ting</i> tiap ada request baru. Klik TERIMA untuk buka sesi private.</p>
<div id="queueList"><p class="text-muted">Tidak ada antrean.</p></div>
</div>
</div>
</div>
{{-- SESI AKTIF --}}
<div class="col-md-7 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h5>Sesi Aktif</h5>
<div id="activeBox"><p class="text-muted">Belum ada sesi. Terima salah satu antrean.</p></div>
<div id="talkHint" class="alert alert-info mt-3 mb-0">
Voice tetap lewat hardware/headset via Go relay UDP :5000.
Dashboard ini <b>kontrolnya</b> (terima / end / panggil).
Tombol fisik <kbd>TALK</kbd> = tahan buat ngomong (PTT half-duplex).
</div>
</div>
</div>
</div>
</div>
{{-- STATUS SEMUA UNIT --}}
<div class="row">
<div class="col-md-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h5>Status Unit</h5>
<div id="deviceGrid" class="row"></div>
</div>
</div>
</div>
</div>
{{-- RIWAYAT --}}
<div class="row">
<div class="col-md-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h5>Riwayat 50 terakhir</h5>
<div class="table-responsive">
<table class="table table-striped" id="historyTable">
<thead>
<tr><th>ID</th><th>Unit</th><th>Arah</th><th>Status</th><th>Diminta</th><th>Durasi</th><th>Rekaman</th></tr>
</thead>
<tbody id="historyBody"><tr><td colspan="7" class="text-muted">Memuat…</td></tr></tbody>
</table>
</div>
<p class="text-muted small mt-2">Rekaman masih raw PCM 16kHz mono — klik unduh lalu putar: <code>ffplay -f s16le -ar 16000 file.raw</code></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('javascript')
<script>
const API = '/api/v1/intercom';
// Helper tanpa jQuery (layouts.master tidak selalu expose $ global)
function el(id) { return document.getElementById(id); }
function setBadge(ok, txt) {
const b = el('srvStatus');
b.setAttribute('class', 'badge ' + (ok ? 'badge-success' : 'badge-danger'));
b.textContent = txt;
}
async function fetchJSON(url, opts) {
const r = await fetch(url, opts);
if (!r.ok) throw new Error('HTTP ' + r.status + ' @ ' + url);
return r.json();
}
let seenWaiting = new Set();
let audioCtx = null;
function beep() {
try {
audioCtx = audioCtx || new (window.AudioContext || window.webkitAudioContext)();
const o = audioCtx.createOscillator(), g = audioCtx.createGain();
o.connect(g); g.connect(audioCtx.destination);
o.frequency.value = 880; o.type = 'sine';
g.gain.setValueAtTime(0.25, audioCtx.currentTime);
o.start(); o.stop(audioCtx.currentTime + 0.35);
} catch (e) {}
}
function elapsed(ts) {
if (!ts) return '-';
const s = Math.max(0, Math.floor((Date.now() - new Date(ts).getTime()) / 1000));
const m = String(Math.floor(s / 60)).padStart(2, '0');
return m + ':' + String(s % 60).padStart(2, '0');
}
function badge(st) {
const map = { idle: 'st-idle', calling: 'st-calling', waiting: 'st-calling', active: 'st-active', busy: 'st-busy', offline: 'st-offline', ended: 'st-offline', missed: 'st-busy' };
return '<span class="badge-status ' + (map[st] || 'st-offline') + '">' + (st || '-').toUpperCase() + '</span>';
}
async function loadQueue() {
try {
const q = await fetchJSON(API + '/queue');
setBadge(true, '● online');
const waiting = q.filter(x => x.status === 'waiting');
const active = q.filter(x => x.status === 'active');
el('queueCount').textContent = waiting.length;
// bunyi kalau ada waiting baru
waiting.forEach(w => { if (!seenWaiting.has(w.id)) { seenWaiting.add(w.id); beep(); } });
el('queueList').innerHTML = waiting.length ? '' : '<p class="text-muted">Tidak ada antrean.</p>';
waiting.forEach(w => {
el('queueList').insertAdjacentHTML('beforeend',
'<div class="card queue-card mb-2"><div class="card-body py-2 d-flex justify-content-between align-items-center">' +
'<div><b>' + w.device_id + '</b> <span class="timer">' + elapsed(w.requested_at) + '</span><br>' +
'<small class="text-muted">' + (w.arah === 'out' ? '⬅ pusat memanggil' : '➡ client mengetok') + ' • #' + w.id + '</small></div>' +
'<button class="btn btn-sm btn-warning" onclick="acceptCall(' + w.id + ')">TERIMA</button>' +
'</div></div>'
);
});
if (active.length) {
const a = active[0];
el('activeBox').innerHTML =
'<div class="card queue-card active-session"><div class="card-body">' +
'<h3>' + a.device_id + ' <span class="timer" id="activeTimer">' + elapsed(a.started_at || a.requested_at) + '</span></h3>' +
'<p class="text-muted mb-2">Sesi #' + a.id + ' • private, unit lain tidak dengar</p>' +
'<button class="btn btn-danger" onclick="endCall(' + a.id + ')">END</button> ' +
'<span class="text-muted small ml-2">atau tombol END fisik di box pusat</span>' +
'</div></div>';
} else {
el('activeBox').innerHTML = '<p class="text-muted">Belum ada sesi. Terima salah satu antrean.</p>';
}
} catch (e) {
setBadge(false, '● ' + e.message);
}
}
async function loadDevices() {
try {
const devs = await fetchJSON(API + '/devices');
el('deviceGrid').innerHTML = '';
devs.forEach(d => {
el('deviceGrid').insertAdjacentHTML('beforeend',
'<div class="col-md-2 col-6 mb-2"><div class="border dev-card">' +
'<div class="dev-id">' + d.device_id + '</div>' +
'<div class="small text-muted">' + (d.nama || '') + '</div>' +
'<div class="my-1">' + badge(d.status) + '</div>' +
'<button class="btn btn-sm btn-primary btn-block mt-1" onclick="callDevice(\'' + d.device_id + '\')">PANGGIL</button>' +
'</div></div>'
);
});
if (!devs.length) el('deviceGrid').innerHTML = '<p class="text-muted">Belum ada device. Seed C1–C5 + ADMIN dulu.</p>';
} catch (e) {}
}
async function loadHistory() {
try {
const h = await fetchJSON(API + '/history?limit=50');
el('historyBody').innerHTML = '';
h.forEach(r => {
const rec = r.recording_path
? '<a href="/intercom/recording/' + r.id + '" class="btn btn-sm btn-light border">⬇ unduh</a>'
: '<span class="text-muted">-</span>';
el('historyBody').insertAdjacentHTML('beforeend', '<tr><td>#' + r.id + '</td><td><b>' + r.device_id + '</b></td><td>' + r.arah + '</td><td>' + badge(r.status) + '</td><td class="small">' + (r.requested_at || '-') + '</td><td>' + (r.durasi_detik || 0) + 's</td><td>' + rec + '</td></tr>');
});
} catch (e) {}
}
async function acceptCall(id) {
await fetch(API + '/accept', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ call_id: id }) });
loadAll();
}
async function endCall(id) {
await fetch(API + '/end', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ call_id: id, ended_by: 'admin' }) });
loadAll();
}
// Panggil manual pusat -> client: buat request arah out lalu langsung accept (sesi private langsung kebuka)
async function callDevice(dev) {
if (dev === 'ADMIN') return;
const r = await (await fetch(API + '/request', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ device_id: dev, arah: 'out' }) })).json();
if (r.call_id) await fetch(API + '/accept', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ call_id: r.call_id }) });
loadAll();
}
function loadAll() { loadQueue(); loadDevices(); loadHistory(); }
loadAll();
setInterval(loadQueue, 1000);
setInterval(loadDevices, 2000);
setInterval(loadHistory, 5000);
</script>
@endsection
@@ -0,0 +1,379 @@
@extends('layouts.master')
@section('styles')
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.8/css/jquery.dataTables.min.css">
<style>
thead .th {
align-text: center;
}
</style>
@endsection
@section('content')
<div class="container-fluid">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h4 class="mb-1">Audit Report</h4>
<small class="text-muted">
Transaksi dengan status transaksi bermasalah
</small>
</div>
</div>
{{-- FILTER --}}
<div class="card">
<div class="card-body">
{{-- FILTER --}}
<div class="row g-3 mb-3">
<div class="col-md-3">
<label class="form-label">
Tanggal Mulai
</label>
<input
type="date"
id="start_date"
class="form-control"
value="{{ date('Y-m-d') }}"
>
</div>
<div class="col-md-3">
<label class="form-label">
Tanggal Akhir
</label>
<input
type="date"
id="end_date"
class="form-control"
value="{{ date('Y-m-d') }}"
>
</div>
<div class="col-md-2">
<label class="form-label">
Status
</label>
<select
id="status_transaksi"
class="form-select"
>
<option value="0">Semua</option>
<option value="-1">Pembatalan</option>
<option value="-2">Maintenance</option>
</select>
</div>
<div class="col-md-2">
<label class="form-label">
Alasan
</label>
<select
id="filter_alasan"
class="form-select"
>
<option value="1">
Batal
</option>
<option value="0">
Semua
</option>
</select>
</div>
<div class="col-md-2 d-flex align-items-end">
<button
type="button"
class="btn btn-primary w-100"
id="btn-search"
>
<i class="fas fa-search"></i>
Cari
</button>
</div>
</div>
{{-- SUMMARY --}}
<div class="row mb-3">
<div class="col-md-3">
<div class="border rounded p-3">
<small>Total Transaksi</small>
<h4 id="total-transaksi">
0
</h4>
</div>
</div>
<div class="col-md-3">
<div class="border rounded p-3">
<small>Total Bayar</small>
<h4 id="total-bayar">
Rp 0
</h4>
</div>
</div>
</div>
{{-- TABLE --}}
<div class="table-responsive">
<table
class="table table-bordered table-hover"
id="audit-table"
>
<thead>
<tr>
<th>ID</th>
<th>Nomer Polisi</th>
<th>Waktu Masuk</th>
<th>Waktu Keluar</th>
<th>Status</th>
<th>Bayar Keluar</th>
<th>Alasan</th>
<th>Jenis Pembayaran</th>
<th>Petugas</th>
</tr>
</thead>
<tbody id="audit-table-body">
{{-- kosong saat pertama dibuka --}}
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection
@section('javascript')
<script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
<script>
$(function () {
$('#btn-search').on('click', function () {
const btn = $(this);
const startDate = $('#start_date').val();
const endDate = $('#end_date').val();
const status = $('#status_transaksi').val();
const alasan = $('#filter_alasan').val();
if (!startDate || !endDate) {
alert('Tanggal harus diisi');
return;
}
if (startDate > endDate) {
alert(
'Tanggal mulai tidak boleh lebih besar dari tanggal akhir'
);
return;
}
$.ajax({
url: "{{ route('audit.data') }}",
type: "GET",
data: {
start_date: startDate,
end_date: endDate,
status_transaksi: status,
filter_alasan: alasan
},
dataType: "json",
beforeSend: function () {
btn.prop('disabled', true);
$('#audit-table-body').html(`
<tr>
<td colspan="8"
class="text-center">
Memuat data...
</td>
</tr>
`);
},
success: function (response) {
if (!response.response) {
return;
}
const rows = response.data;
$('#audit-table-body').empty();
if (rows.length === 0) {
$('#audit-table-body').html(`
<tr>
<td colspan="8"
class="text-center">
Tidak ada data
</td>
</tr>
`);
} else {
rows.forEach(function (row) {
$('#audit-table-body').append(`
<tr>
<td class="text-center">
${row.id ?? '-'}
</td>
<td class="text-center">
${row.no_pol ?? '-'}
</td>
<td class="text-center">
${row.waktu_masuk ?? '-'}
</td class="text-center">
<td class="text-center">
${row.waktu_keluar ?? '-'}
</td>
<td class="text-center">
<span class="badge bg-danger">
${row.status_transaksi ?? '-'}
</span>
</td>
<td class="text-end">
Rp ${Number(
row.bayar_keluar ?? 0
).toLocaleString('id-ID')}
</td>
<td class="text-center">
${row.alasan ?? '-'}
</td>
<td class="text-center">
${row.cara_bayar == 2 ? 'Tunai' : 'Non Tunai'}
</td>
<td class="text-center">
${row.operator ?? '-'}
</td>
</tr>
`);
});
}
// SUMMARY
$('#total-transaksi').text(
Number(
response.summary.total_transaksi
).toLocaleString('id-ID')
);
$('#total-bayar').text(
'Rp ' +
Number(
response.summary.total_bayar
).toLocaleString('id-ID')
);
},
error: function (xhr) {
console.error(xhr.responseText);
$('#audit-table-body').html(`
<tr>
<td colspan="8"
class="text-center text-danger">
Gagal mengambil data
</td>
</tr>
`);
},
complete: function () {
btn.prop('disabled', false);
}
});
});
function payment(value) {
switch (value) {
case 2:
return 'Tunai';
case 3:
return 'Non Tunai';
default:
return 'Jenis Pembayaran Tidak ditemukan'
}
};
function vehicleStatus(value) {
switch (value) {
case 0:
return 'Sudah Keluar';
case 1:
return 'Masih didalam';
case -2:
return 'Maintenance';
default:
return 'Tidak Ada'
}
}
});
</script>
@endsection
@@ -0,0 +1,143 @@
@extends('layouts.master') {{-- sesuaikan dengan layout master project kamu --}}
@section('content')
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0">Data Stasiun Kerja</h4>
<button type="button" class="btn btn-primary" id="btnTambah">
+ Tambah Stasiun Kerja
</button>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover align-middle" id="tabelStasiunKerja">
<thead class="table-light">
<tr>
<th>Kode</th>
<th>Nama</th>
<th>Margin</th>
<th>SPV</th>
<th>Target</th>
<th>Lokasi</th>
<th>Lantai</th>
<th>Status</th>
<th>Alamat</th>
<th>Bank</th>
<th>Telp</th>
<th>Kode Akses</th>
<th style="width: 130px;">Aksi</th>
</tr>
</thead>
<tbody id="tbodyStasiunKerja">
{{-- diisi lewat JS --}}
</tbody>
</table>
</div>
</div>
{{-- ===================== MODAL FORM ===================== --}}
<div class="modal fade" id="modalStasiunKerja" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form id="formStasiunKerja">
<div class="modal-header">
<h5 class="modal-title" id="modalStasiunKerjaTitle">Tambah Stasiun Kerja</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputMode" value="create">
<input type="hidden" id="inputKodeOriginal" value="">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Kode <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="inputKode" maxlength="50" required>
<div class="invalid-feedback" data-error-for="kode"></div>
</div>
<div class="col-md-8">
<label class="form-label">Nama <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="inputNama" maxlength="255" required>
<div class="invalid-feedback" data-error-for="nama"></div>
</div>
<div class="col-md-3">
<label class="form-label">Margin</label>
<input type="number" class="form-control" id="inputMargin">
<div class="invalid-feedback" data-error-for="margin"></div>
</div>
<div class="col-md-3">
<label class="form-label">Target</label>
<input type="number" class="form-control" id="inputTarget">
<div class="invalid-feedback" data-error-for="target"></div>
</div>
<div class="col-md-3">
<label class="form-label">Status</label>
<select class="form-select" id="inputStatus">
<option value="1">Aktif</option>
<option value="0">Nonaktif</option>
</select>
<div class="invalid-feedback" data-error-for="status"></div>
</div>
<div class="col-md-3">
<label class="form-label">Kode Akses</label>
<input type="text" class="form-control" id="inputKodeAkses" maxlength="5">
<div class="invalid-feedback" data-error-for="kode_akses"></div>
</div>
<div class="col-md-4">
<label class="form-label">SPV</label>
<input type="text" class="form-control" id="inputSpv" maxlength="20">
<div class="invalid-feedback" data-error-for="spv"></div>
</div>
<div class="col-md-4">
<label class="form-label">ID Lokasi</label>
<input type="text" class="form-control" id="inputIdLokasi" maxlength="10">
<div class="invalid-feedback" data-error-for="id_lokasi"></div>
</div>
<div class="col-md-4">
<label class="form-label">Lantai</label>
<input type="text" class="form-control" id="inputLantai" maxlength="4">
<div class="invalid-feedback" data-error-for="lantai"></div>
</div>
<div class="col-md-6">
<label class="form-label">Bank</label>
<input type="text" class="form-control" id="inputBank" maxlength="64">
<div class="invalid-feedback" data-error-for="bank"></div>
</div>
<div class="col-md-6">
<label class="form-label">Telp</label>
<input type="text" class="form-control" id="inputTelp" maxlength="32">
<div class="invalid-feedback" data-error-for="telp"></div>
</div>
<div class="col-md-12">
<label class="form-label">Alamat</label>
<textarea class="form-control" id="inputAlamat" rows="2" maxlength="255"></textarea>
<div class="invalid-feedback" data-error-for="alamat"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-primary" id="btnSimpan">Simpan</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('javascript')
<script>
// Endpoint diambil dari Laravel route(), disuntik ke JS supaya
// tidak hardcode URL (menghindari masalah beda host/port seperti sebelumnya).
window.STASIUNKERJA_ENDPOINT = "{{ url('/setup/master/stasiunkerja') }}";
</script>
<script src="{{ asset('js/setup/stasiunkerja.js') }}"></script>
@endsection
@@ -0,0 +1,150 @@
@extends('layouts.master') {{-- sesuaikan dengan layout master project kamu --}}
@section('content')
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0">Data Tarif Stiker</h4>
<button type="button" class="btn btn-primary" id="btnTambah">
+ Tambah Tarif Stiker
</button>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover align-middle" id="tabelTarifStiker">
<thead class="table-light">
<tr>
<th id="thTarifId" role="button" style="cursor:pointer; user-select:none; white-space:nowrap;">
Tarif ID <span id="sortIconTarifId">▼</span>
</th>
<th>ID Mobil</th>
<th>Jenis Langganan</th>
<th>Tarif</th>
<th>Jenis</th>
<th>Jenis Member</th>
<th>Langganan (Stasiun Kerja)</th>
<th>Online</th>
<th>Kode Akses</th>
<th style="width: 130px;">Aksi</th>
</tr>
</thead>
<tbody id="tbodyTarifStiker">
{{-- diisi lewat JS --}}
</tbody>
</table>
</div>
</div>
{{-- ===================== MODAL FORM ===================== --}}
<div class="modal fade" id="modalTarifStiker" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<form id="formTarifStiker">
<div class="modal-header">
<h5 class="modal-title" id="modalTarifStikerTitle">Tambah Tarif Stiker</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputMode" value="create">
<input type="hidden" id="inputTarifIdOriginal" value="">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Tarif ID</label>
<input type="number" class="form-control" id="inputTarifId" placeholder="Kosongkan untuk auto">
<div class="form-text">Kosongkan saat tambah data baru, akan digenerate otomatis.</div>
<div class="invalid-feedback" data-error-for="tarif_id"></div>
</div>
<div class="col-md-4">
<label class="form-label">ID Mobil <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="inputIdMobil" maxlength="10" required>
<div class="invalid-feedback" data-error-for="id_mobil"></div>
</div>
<div class="col-md-4">
<label class="form-label">Tarif <span class="text-danger">*</span></label>
<input type="number" step="0.01" class="form-control" id="inputTarif" required>
<div class="invalid-feedback" data-error-for="tarif"></div>
</div>
<div class="col-md-6">
<label class="form-label">Jenis Langganan <span class="text-danger">*</span></label>
<select class="form-select" id="inputJenisLangganan" required>
<option value="">-- Pilih Jenis Langganan --</option>
@foreach ($jenisLanggananList as $jl)
<option value="{{ $jl->id }}">{{ $jl->nama }}</option>
@endforeach
</select>
<div class="invalid-feedback" data-error-for="jenis_langganan"></div>
</div>
<div class="col-md-6">
<label class="form-label">Langganan (Stasiun Kerja)</label>
<select class="form-select" id="inputLangganan">
<option value="">-- Tidak terikat stasiun kerja --</option>
@foreach ($stasiunKerjaList as $sk)
<option value="{{ $sk->kode }}">{{ $sk->nama }} ({{ $sk->kode }})</option>
@endforeach
</select>
<div class="invalid-feedback" data-error-for="langganan"></div>
</div>
<div class="col-md-3">
<label class="form-label">Jenis</label>
<input type="text" class="form-control" id="inputJenis" maxlength="30">
<div class="invalid-feedback" data-error-for="jenis"></div>
</div>
<div class="col-md-3">
<label class="form-label">Jenis Member</label>
<input type="text" class="form-control" id="inputJenisMember" maxlength="50">
<div class="invalid-feedback" data-error-for="jenis_member"></div>
</div>
<div class="col-md-3">
<label class="form-label">Last Member</label>
<input type="text" class="form-control" id="inputLastMember" maxlength="5">
<div class="invalid-feedback" data-error-for="last_member"></div>
</div>
<div class="col-md-3">
<label class="form-label">Tgl Akhir</label>
<input type="text" class="form-control" id="inputTglAkhir" maxlength="2">
<div class="invalid-feedback" data-error-for="tgl_akhir"></div>
</div>
<div class="col-md-4">
<label class="form-label">Hitung Awal Bulan</label>
<input type="number" class="form-control" id="inputHitungAwalBulan">
<div class="invalid-feedback" data-error-for="hitung_awal_bulan"></div>
</div>
<div class="col-md-4">
<label class="form-label">Kode Akses</label>
<input type="text" class="form-control" id="inputKodeAkses" maxlength="5">
<div class="invalid-feedback" data-error-for="kode_akses"></div>
</div>
<div class="col-md-4 d-flex align-items-end">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="inputAvailableOnline">
<label class="form-check-label" for="inputAvailableOnline">
Tersedia Online
</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="submit" class="btn btn-primary" id="btnSimpan">Simpan</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@section('javascript')
<script>
window.TARIFSTIKER_ENDPOINT = "{{ url('/setup/master/tarif-stiker') }}";
</script>
<script src="{{ asset('js/setup/tarifstiker.js') }}"></script>
@endsection
@@ -10,7 +10,7 @@
<div class="entry-container">
{{-- <img src="{{ asset('images/parkq-systems.png') }}"
{{-- <img src="{{ asset('images/parkq-systems.png') }}"
alt="Logo parkq-systems"
class="img-fluid logo-img"> --}}
@@ -31,6 +31,11 @@ class="img-fluid logo-img"> --}}
<div class="member-info">
<div class="info-row">
<span class="label">No. Transaksi</span>
<span id="act-notrans" class="value"></span>
</div>
<div class="info-row">
<span class="label">Nama</span>
<span id="act-nama" class="value"></span>
@@ -52,11 +57,11 @@ class="img-fluid logo-img"> --}}
<div class="active-warning">
Member masih aktif
</div>
<p class="text-warning">silahkan pilih produk di bawah ini :</p>
<div class="select-input">
<select id="produkSelect" class="form-control form-select">
</select>
</div>
@@ -75,6 +80,12 @@ class="img-fluid logo-img"> --}}
<div class="member-info">
<div class="info-row">
<span class="label">No. Transaksi</span>
<span id="exp-notrans" class="value"></span>
<span id="exp-notrans-value" class="value" hidden></span>
</div>
<div class="info-row">
<span class="label">Nama</span>
<span id="exp-nama" class="value"></span>
@@ -99,10 +110,10 @@ class="img-fluid logo-img"> --}}
<div class="select-input">
<select id="produkSelect" class="form-control form-select">
</select>
</div>
<button class="btn-submit btn-renew">
PERPANJANG MEMBER
@@ -145,7 +156,7 @@ function submitNopol(){
if(!nopol) return;
fetch("/api/member/" + encodeURIComponent(nopol))
fetch("/api/memberin/" + encodeURIComponent(nopol))
.then(res => res.json())
.then(data => {
@@ -163,12 +174,13 @@ function submitNopol(){
console.log(data);
document.getElementById("form-active").style.display="block";
document.getElementById("act-notrans").innerText = data.data.notrans;
document.getElementById("act-nama").innerText = data.data.nama;
document.getElementById("act-nopol").innerText = data.data.vehicle + " - " + data.data.nopol;
document.getElementById("act-periode").innerText = formatTanggal(data.data.awal.split(' ')[0]) + " - " + formatTanggal(data.data.akhir.split(' ')[0])
// data.data.awal.split(' ')[0] + " - " + data.data.akhir.split(' ')[0];
renderProduk(jLangganan, jenisMobil);
}
/* expired */
@@ -179,6 +191,8 @@ function submitNopol(){
console.log(data);
document.getElementById("form-expired").style.display="block";
document.getElementById("exp-notrans").innerText = data.data.notrans.slice(0, 13);
document.getElementById("exp-notrans-value").innerText = data.data.notrans;
document.getElementById("exp-nama").innerText = data.data.nama;
document.getElementById("exp-nopol").innerText = data.data.vehicle + " - " + data.data.nopol;
document.getElementById("exp-periode").innerText = formatTanggal(data.data.awal.split(' ')[0]) + " - " + formatTanggal(data.data.akhir.split(' ')[0])
@@ -206,7 +220,7 @@ function submitNopol(){
}
function renderProduk(stasiunId, jenisMobil){
console.log(stasiunId);
@@ -261,4 +275,4 @@ function formatTanggal(tgl){
});
</script>
@endsection
@endsection
+13 -5
View File
@@ -11,10 +11,15 @@
<h2 class="text-center fw-bold">All New Manufacture</h2>
<div class="card mb-0 h-auto">
<div class="card-body">
<div class="text-center mb-3">
<h4 class="text-center mb-4">Silahkan login untuk mengakses halaman</h4>
</div>
<form method="POST" action="{{ route('login') }}">
<div class="text-center mb-3">
<h4 class="text-center mb-4">Silahkan login untuk mengakses halaman</h4>
</div>
@if ($errors->any())
<div class="alert alert-danger py-2 mb-3" role="alert">
<i class="fa fa-triangle-exclamation me-2"></i>{{ $errors->first() }}
</div>
@endif
<form method="POST" action="{{ route('login') }}">
@csrf
{{-- <div class="form-group mb-4"> --}}
<div class="mb-sm-4 mb-3 position-relative">
@@ -28,11 +33,14 @@
</div>
<div class="mb-sm-4 mb-3 position-relative">
<label class="form-label" for="dlab-password">Password</label>
<input type="password" id="password" class="form-control" value="123456" @error('password') is-invalid @enderror name="password" required autocomplete="current-password">
<input type="password" id="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">
<span class="show-pass eye">
<i class="fa fa-eye-slash"></i>
<i class="fa fa-eye"></i>
</span>
@error('password')
<span class="invalid-feedback" role="alert"><strong>{{ $message }}</strong></span>
@enderror
</div>
<div class="row mb-0">
<div class="col-md-12 text-center">
+6 -16
View File
@@ -27,19 +27,19 @@
<div class="pe-1 mb-3 mb-xl-0">
<button type="button" class="btn btn-outline-inverse-info btn-icon-text">
Feedback
<i class="mdi mdi-message-outline btn-icon-append"></i>
<i class="mdi mdi-message-outline btn-icon-append"></i>
</button>
</div>
<div class="pe-1 mb-3 mb-xl-0">
<button type="button" class="btn btn-outline-inverse-info btn-icon-text">
Help
<i class="mdi mdi-help-circle-outline btn-icon-append"></i>
<i class="mdi mdi-help-circle-outline btn-icon-append"></i>
</button>
</div>
<div class="pe-1 mb-3 mb-xl-0">
<button type="button" class="btn btn-outline-inverse-info btn-icon-text">
Print
<i class="mdi mdi-printer btn-icon-append"></i>
<i class="mdi mdi-printer btn-icon-append"></i>
</button>
</div>
</div>
@@ -107,11 +107,11 @@
<div class="col-lg-4 mb-3 mb-lg-0">
<div class="card congratulation-bg text-center">
<div class="card-body pb-0">
<img src="images/dashboard/face29.png" alt="">
<img src="images/dashboard/face29.png" alt="">
<h2 class="mt-3 text-white mb-3 font-weight-bold">Congratulations
Johnson
</h2>
<p>You have done 57.6% more sales today.
<p>You have done 57.6% more sales today.
Check your new badge in your profile.
</p>
</div>
@@ -426,17 +426,7 @@
</div>
</div>
<!-- content-wrapper ends -->
<!-- partial:partials/_footer.html -->
<footer class="footer">
<div class="footer-wrap">
<div class="d-sm-flex justify-content-center justify-content-sm-between">
<span class="text-muted text-center text-sm-left d-block d-sm-inline-block">Copyright © <a href="https://www.bootstrapdash.com/" target="_blank">bootstrapdash.com </a>2021</span>
<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">Only the best <a href="https://www.bootstrapdash.com/" target="_blank"> Bootstrap dashboard </a> templates</span>
</div>
</div>
</footer>
<!-- partial -->
</div>
<!-- main-panel ends -->
</div>
@endsection
@endsection
+17 -5
View File
@@ -22,12 +22,24 @@
<body>
<div class="container-scroller">
<!-- partial:partials/_horizontal-navbar.html -->
@include('layouts.partial.horizontal-menu')
@yield('content')
<!-- page-body-wrapper ends -->
</div>
<!-- partial:partials/_footer.html -->
<footer class="footer">
<div class="footer-wrap">
<div class="d-sm-flex justify-content-center justify-content-sm-between">
<span class="text-muted text-center text-sm-left d-block d-sm-inline-block">Copyright © <a href="https://www.bootstrapdash.com/" target="_blank">bootstrapdash.com </a>2021</span>
<span class="float-none float-sm-right d-block mt-1 mt-sm-0 text-center">Only the best <a href="https://www.bootstrapdash.com/" target="_blank"> Bootstrap dashboard </a> templates</span>
</div>
</div>
</footer>
<!-- partial -->
<!--</div>-->
<!-- container-scroller -->
<!-- base:js -->
<script src="{{ asset('vendors/base/vendor.bundle.base.js') }}"></script>
@@ -42,7 +54,7 @@
<script src="{{ asset('vendors/chart.js/Chart.min.js') }}"></script>
<script src="{{ asset('vendors/progressbar.js/progressbar.min.js') }}"></script>
<script src="{{ asset('vendors/chartjs-plugin-datalabels/chartjs-plugin-datalabels.js') }}"></script>
<script src="{{ asset('vendors/justgage/raphael-2.1.4.min.js') }}"></script>
<script src="{{ asset('vendors/justgage/raphael-2.1.4.min.js') }}"></script>
<script src="{{ asset('vendors/justgage/justgage.js') }}"></script>
<script src="{{ asset('js/jquery.cookie.js') }}" type="text/javascript"></script>
<!-- Custom js for this page-->
@@ -50,4 +62,4 @@
<!-- End custom js for this page-->
@yield('javascript')
</body>
</html>
</html>
@@ -0,0 +1,3 @@
<div>
{{-- If your happiness depends on money, you will never be happy with yourself. --}}
</div>
+70
View File
@@ -0,0 +1,70 @@
@extends('layouts.master')
@section('title', 'DHCP Reservations')
@section('content')
<div class="row">
<div class="col-md-12">
<div class="card mg-b-20">
<div class="card-header">
<div class="card-title">
<h3 class="card-title mb-0">
Lot Parking
</h3>
</div>
</div>
<div class="card-body">
{{-- Container yang akan di-refresh --}}
<div id="parking-container">
@include('livewire.partial-lot', ['parkingDataByArea' => $parkingDataByArea])
</div>
</div>
</div>
</div>
</div>
@endsection
@section('javascript')
<script>
document.addEventListener('DOMContentLoaded', function () {
const parkingContainer = document.getElementById('parking-container');
const refreshUrl = "{{ route('parking-lot.data') }}";
async function refreshParking() {
try {
const response = await fetch(refreshUrl, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'text/html'
}
});
if (!response.ok) {
throw new Error('HTTP Error: ' + response.status);
}
const html = await response.text();
parkingContainer.innerHTML = html;
} catch (error) {
console.error(
'Gagal mengambil data parking:',
error
);
}
}
// Refresh setiap 5 detik
setInterval(refreshParking, 5000);
});
</script>
@endsection
@@ -0,0 +1,44 @@
<div>
<script>
document.addEventListener('livewire:load', function () {
// console.log('Livewire component loaded');
setInterval(function () {
@this.call('fetchParkingData');
}, 5000);
// Livewire.on('refresh', (time) => {
// console.log('Data refreshed at:', time);
// alert('Data refreshed at: ' + time);
// });
});
</script>
<div class="row">
<div class="col-md-12">
<div class="card mg-b-20">
<div class="card-header">
<div class="card-title">
<h3 class="card-title">Lot Parking</h3>
</div>
</div>
<div class="card-body">
<div class="row">
@foreach ($parkingDataByArea as $areaData)
<div class="col-md-6 card-content mb-5 px-3">
<h3 style="text-align: center;">Lantai: {{ $areaData['lantai'] }}</h3>
<div class="container mt-3">
<div class="row img-container">
@foreach ($areaData['data'] as $log)
<div class="col-md-6 img-item">
<img src="{{ $log->status == 1 ? asset('build/assets/images/users/red_car.png') : asset('build/assets/images/users/green_car.png') }}" alt="{{ $log->status == 1 ? 'Red Car' : 'Green Car' }}">
<span class="d-block">{{ $log->lot_number }}</span>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,42 @@
<div class="row">
@foreach ($parkingDataByArea as $areaData)
<div class="col-md-6 card-content mb-5 px-3">
<h3 class="text-center">
Lantai: {{ $areaData['lantai'] }}
</h3>
<div class="container mt-3">
<div class="row img-container">
@foreach ($areaData['data'] as $log)
<div class="col-md-6 img-item text-center mb-3">
<img
src="{{ $log->status == 1
? asset('images/lot/red_car.png')
: asset('images/lot/green_car.png') }}"
alt="{{ $log->status == 1 ? 'Red Car' : 'Green Car' }}"
>
<span class="d-block">
{{ $log->lot_number }}
</span>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
</div>
+200
View File
@@ -0,0 +1,200 @@
@extends('layouts.master')
@section('title', 'Parking Space Information')
@section('styles')
<!-- Treeview Css -->
{{-- <link rel="stylesheet" href="{{asset('build/assets/libs/js-treeview/treeview.min.css')}}"> --}}
<style>
.img-container {
display: flex;
flex-wrap: wrap;
}
.img-item {
flex: 1 0 8.33%; /* 100% / 12 = 8.33% */
max-width: 8.33%;
padding: 5px;
}
.img-item img {
width: 100%;
height: auto;
/* display: block;
margin-top: 10px; */
}
.img-item span {
display: block;
margin-top: 10px; /* Atur jarak antara gambar dan teks jika diperlukan */
}
</style>
@endsection
@section('content')
<!-- PAGE-HEADER -->
<!--<div class="page-header d-flex align-items-center justify-content-between border-bottom mb-4">
<h1 class="page-title">Parking Lot Space</h1>
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="javascript:void(0);">Admin UI</a></li>
<li class="breadcrumb-item active" aria-current="page">Treeview</li>
</ol>
</div>
</div>-->
<!-- PAGE-HEADER END -->
<!-- CONTAINER -->
<div class="main-container container-fluid">
<!-- CONTAINER -->
{{-- <div class="main-container container-fluid">
<!-- ROW OPEN -->
<div class="row">
<div class="col-md-12">
<div class="card mg-b-20">
<div class="card-header">
<div class="card-title">
<h3 class="card-title">Lot Parking</h3>
</div>
</div>
<div class="card-body">
<div class="row">
<!-- Konten akan diperbarui oleh AJAX -->
</div>
</div>
</div>
</div>
</div>
<!-- ROW CLOSED -->
</div> --}}
<!-- ROW OPEN -->
<div class="row">
<div class="col-md-12">
<div class="card mg-b-20">
<div class="card-header">
<div class="card-title">
<h3 class="card-title">Lot Parking</h3>
</div>
</div>
<div class="card-body">
<div class="row" id="lot-space">
</div>
</div>
</div>
</div>
</div>
<!-- ROW CLOSED -->
{{-- <!-- ROW OPEN -->
<div class="row">
<div class="col-md-12">
<div class="card mg-b-20">
<div class="card-header">
<div class="card-title">
<h3 class="card-title">Treeview</h3>
</div>
</div>
<div class="card-body">
<div class="card-content">
<div class="row">
@foreach ($parkingDataByArea as $areaData)
<h3>Lantai: {{ $areaData['lantai'] }}</h3>
<div class="col-sm-12 col-md-12 col-lg-12 col-xl-3">
<div class="d-flex flex-wrap gap-2">
@foreach ($areaData['data'] as $log)
<div class="m-2">
@if ($log->status == 1)
<span class="avatar avatar-md bradius cover-image" data-bs-image-src="{{ asset('images/lot/red_car.png') }}"></span>
<li>Lot Number: {{ $log->lot_number }} - Status: {{ $log->status }}</li>
@else
<span class="avatar avatar-md bradius cover-image" data-bs-image-src="{{ asset('images/lot/green_car.png') }}"></span>
<li>Lot Number: {{ $log->lot_number }} - Status: {{ $log->status }}</li>
@endif
</div>
@endforeach
</div>
</div>
@endforeach
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ROW CLOSED --> --}}
</div>
<!-- CONTAINER END -->
@endsection
@section('javascript')
<!-- Tambahkan ini dalam template blade Anda -->
<script>
document.addEventListener('DOMContentLoaded', function () {
function fetchParkingData() {
fetch('{{ route("parking-lot.data") }}')
.then(response => {
if (!response.ok) {
throw new Error('HTTP Error: ' + response.status);
}
return response.json();
})
.then(data => {
const container = document.getElementById('lot-space');
container.innerHTML = '';
data.forEach(function (areaData) {
let html = `
<div class="col-md-6 card-content mb-5 px-3">
<h3 class="text-center">
Parking Space : ${areaData.lantai}
</h3>
<div class="container mt-3">
<div class="row img-container">
`;
areaData.data.forEach(function (log) {
const image = log.status == 1
? '{{ asset("images/lot/red_car.png") }}'
: '{{ asset("images/lot/green_car.png") }}';
html += `
<div class="col-md-6 img-item">
<img src="${image}" alt="${log.status == 1 ? 'Red Car' : 'Green Car'}">
<span class="d-block">${log.lot_number}</span>
</div>
`;
});
html += `
</div>
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
});
})
.catch(error => {
console.error('Error fetching parking data:', error);
});
}
// Load pertama kali
fetchParkingData();
// Refresh setiap 5 detik
setInterval(fetchParkingData, 5000);
});
</script>
@endsection
@@ -39,66 +39,226 @@
<div class="card-header">
</div>
<div class="card-body">
<form action="">
<div class="row align-items-end g-3">
<form id="filter-parkir" action="javascript:void(0)">
<div class="row align-items-end g-2">
<!-- CARI DATA -->
<div class="col-md-1">
<div class="form-group">
<label for="data">Cari Data</label>
<input type="text" class="form-control" id="data" placeholder="Masukkan data">
<label for="q">Cari Data</label>
<input type="text" class="form-control form-control-sm" id="q" placeholder="Keyword">
</div>
</div>
<div class="col-md-1">
<div class="form-group">
<label>&nbsp;</label> <!-- biar sejajar -->
<select class="form-control js-example-basic-single">
<label for="field">Field</label>
<select class="form-control form-control-sm js-example-basic-single" id="field">
<option value="id" selected>No. ID</option>
<option value="no_pol">No. Pol</option>
<option value="no_barcode">No. Barcode/Card</option>
<option value="no_barcode">No. Transaksi</option>
</select>
</div>
</div>
<!-- TANGGAL -->
<div class="col-md-1">
<!-- PERIODE -->
<div class="col-md-2">
<div class="form-group">
<label for="tanggalCari">Tanggal</label>
<input type="date" class="form-control" id="tanggalCari" name="tanggal_mulai" required>
<label for="tgl_awal">Periode Awal</label>
<input type="date" class="form-control form-control-sm" id="tgl_awal" name="tgl_awal">
</div>
</div>
<!-- TOGGLE HARIAN -->
<div class="col-md-2 d-flex align-items-center gap-2">
<p class="mb-0">Harian</p>
<label class="toggle-switch toggle-switch-success mb-0">
<input type="checkbox" id="harianToggle" checked>
<span class="toggle-slider round"></span>
</label>
<div class="col-md-2">
<div class="form-group">
<label for="tgl_akhir">Periode Akhir</label>
<input type="date" class="form-control form-control-sm" id="tgl_akhir" name="tgl_akhir">
</div>
</div>
<!-- STATUS KENDARAAN : 1 masuk, 0 keluar -->
<div class="col-md-1">
<div class="form-group">
<label for="status_kendaraan">Kendaraan</label>
<select class="form-control form-control-sm js-example-basic-single" id="status_kendaraan">
<option value="" selected>Semua</option>
<option value="1">Masuk</option>
<option value="0">Keluar</option>
</select>
</div>
</div>
<!-- STATUS TRANSAKSI : 0 casual, 3 member, 32 denda -->
<div class="col-md-2">
<div class="form-group">
<label for="status_transaksi">Transaksi</label>
<select class="form-control form-control-sm js-example-basic-single" id="status_transaksi">
<option value="" selected>Semua</option>
<option value="0">Casual</option>
<option value="3">Member</option>
<option value="32">Denda</option>
</select>
</div>
</div>
<!-- AKSI -->
<div class="col-md-2 d-flex align-items-end gap-1">
<button type="submit" class="btn btn-sm btn-primary" id="btn-cari">
<i class="fa fa-search me-1"></i>Cari
</button>
<button type="button" class="btn btn-sm btn-secondary" id="btn-reset">Reset</button>
</div>
<!-- BUTTON EXPORT -->
<div class="col-md-4 text-end">
<div class="btn-list d-inline-flex gap-2">
<div class="col-md-1 text-end">
<div class="btn-list d-inline-flex gap-1">
<button type="button"
class="btn btn-icon btn-success btn-export-excel">
class="btn btn-sm btn-icon btn-success btn-export-excel" title="Export CSV">
<i class="fa fa-file-excel-o"></i>
</button>
<button type="button"
class="btn btn-icon btn-danger btn-export-pdf">
class="btn btn-sm btn-icon btn-danger btn-export-pdf" title="Cetak">
<i class="fa fa-file-pdf-o"></i>
</button>
</div>
</div>
</div>
</form>
<hr class="my-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<h5 class="card-title mb-0">Hasil Transaksi</h5>
<small class="text-muted" id="info-paging"></small>
</div>
<div class="table-responsive">
<table class="table table-bordered table-striped" id="tabel-parkir">
<thead>
<tr>
<th width="4%">No.</th>
<th>ID</th>
<th>No. Pol</th>
<th>Barcode</th>
<th>Kendaraan</th>
<th>Waktu Masuk</th>
<th>Waktu Keluar</th>
<th>Status Kendaraan</th>
<th>Status Transaksi</th>
</tr>
</thead>
<tbody id="body-parkir">
<tr><td colspan="9" class="text-center">Memuat data...</td></tr>
</tbody>
</table>
</div>
<div class="d-flex justify-content-between align-items-center mt-3">
<button class="btn btn-sm btn-outline-primary" id="btn-prev" type="button">&laquo; Sebelumnya</button>
<span class="text-muted" id="label-halaman"></span>
<button class="btn btn-sm btn-outline-primary" id="btn-next" type="button">Berikutnya &raquo;</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('javascript')
<script src="{{ asset('vendors/select2/select2.min.js') }}"></script>
<script src="{{ asset('js/select2.js') }}"></script>
<script>
$(function () {
const dataUrl = '{{ route('transaksiparkir.data', [], false) }}';
let page = 1;
const labelKendaraan = {1: 'Masuk', 0: 'Keluar'};
const labelTransaksi = {0: 'Casual', 3: 'Member', 32: 'Denda'};
function badge(text, color) {
return '<span class="badge bg-' + color + '">' + text + '</span>';
}
function statusKendaraan(v) {
if (v == 1) return badge('Masuk', 'success');
if (v == 0) return badge('Keluar', 'secondary');
return badge(v ?? '-', 'light text-dark');
}
function statusTransaksi(v) {
const t = labelTransaksi[v] !== undefined ? labelTransaksi[v] : (v ?? '-');
const c = v == 0 ? 'primary' : (v == 3 ? 'info' : (v == 32 ? 'warning' : 'light text-dark'));
return badge(t, c);
}
function esc(v) {
return (v === null || v === undefined) ? '' : String(v)
.replace(/&/g, '&amp;').replace(/</g, '&lt;')
.replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function params(extra) {
const p = {
q: $('#q').val().trim(),
field: $('#field').val(),
tgl_awal: $('#tgl_awal').val(),
tgl_akhir: $('#tgl_akhir').val(),
status_kendaraan: $('#status_kendaraan').val(),
status_transaksi: $('#status_transaksi').val(),
page: page
};
return $.param(Object.assign(p, extra || {}));
}
function load() {
$('#body-parkir').html('<tr><td colspan="9" class="text-center">Memuat data...</td></tr>');
$.ajax({
url: dataUrl + '?' + params(),
type: 'GET',
success: function (r) {
if (!r.data.length) {
$('#body-parkir').html('<tr><td colspan="9" class="text-center">Data tidak ditemukan.</td></tr>');
} else {
let html = '';
const offset = (r.meta.current_page - 1) * r.meta.per_page;
r.data.forEach(function (row, i) {
html += '<tr>'
+ '<td class="text-center">' + (offset + i + 1) + '</td>'
+ '<td>' + esc(row.id) + '</td>'
+ '<td>' + esc(row.no_pol) + '</td>'
+ '<td>' + esc(row.no_barcode) + '</td>'
+ '<td>' + esc([row.id_kendaraan, row.nama_kendaraan].filter(Boolean).join(' - ')) + '</td>'
+ '<td>' + esc(row.waktu_masuk) + '</td>'
+ '<td>' + esc(row.waktu_keluar) + '</td>'
+ '<td class="text-center">' + statusKendaraan(row.status) + '</td>'
+ '<td class="text-center">' + statusTransaksi(row.status_transaksi) + '</td>'
+ '</tr>';
});
$('#body-parkir').html(html);
}
$('#info-paging').text('Total ' + r.meta.total + ' data');
$('#label-halaman').text('Halaman ' + r.meta.current_page + ' / ' + Math.max(r.meta.last_page, 1));
$('#btn-prev').prop('disabled', r.meta.current_page <= 1);
$('#btn-next').prop('disabled', r.meta.current_page >= r.meta.last_page);
page = r.meta.current_page;
},
error: function (xhr) {
let msg = 'Gagal memuat data.';
if (xhr.status === 422 && xhr.responseJSON?.errors) {
msg = Object.values(xhr.responseJSON.errors).flat().join(' ');
}
$('#body-parkir').html('<tr><td colspan="9" class="text-center text-danger">' + esc(msg) + '</td></tr>');
}
});
}
$('#filter-parkir').on('submit', function () { page = 1; load(); });
$('#btn-reset').on('click', function () {
$('#filter-parkir')[0].reset();
$('#field, #status_kendaraan, #status_transaksi').val('').trigger('change');
$('#field').val('id').trigger('change');
page = 1; load();
});
$('#btn-prev').on('click', function () { if (page > 1) { page--; load(); } });
$('#btn-next').on('click', function () { page++; load(); });
$('.btn-export-excel').on('click', function () {
window.location = dataUrl + '?' + params({export: 'csv'});
});
$('.btn-export-pdf').on('click', function () { window.print(); });
load();
});
</script>
@endsection
+31
View File
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Persiapan Aplikasi | P-Solution Parking</title>
<style>
:root { --navy:#102a43; --blue:#1f6feb; --mint:#2dd4bf; --ink:#152536; --muted:#6b7b8d; --line:#dce5ef; } *{box-sizing:border-box} body{min-height:100vh;margin:0;color:var(--ink);font-family:Inter,ui-sans-serif,system-ui,sans-serif;background:radial-gradient(circle at 10% 10%,#d8f3f0 0,transparent 30%),linear-gradient(135deg,#f8fbff,#edf3f9)} .shell{width:min(760px,calc(100% - 32px));margin:0 auto;padding:clamp(42px,10vh,96px) 0}.brand{display:flex;gap:12px;align-items:center;color:var(--navy);font-size:14px;font-weight:750;letter-spacing:.04em;text-transform:uppercase}.brand-mark{width:34px;height:34px;display:grid;place-items:center;color:white;font-weight:800;border-radius:11px;background:linear-gradient(135deg,var(--blue),#254f94)}.panel{margin-top:24px;padding:clamp(26px,5vw,46px);background:#fff;border-radius:24px;box-shadow:0 24px 70px #102a431c}h1{max-width:620px;margin:0;color:var(--navy);font-size:clamp(28px,5vw,42px);letter-spacing:-.04em;line-height:1.08}.lead{max-width:610px;margin:15px 0 30px;color:var(--muted);font-size:16px;line-height:1.7}.progress{height:8px;overflow:hidden;border-radius:99px;background:#e8eef5}.progress-bar{height:100%;border-radius:inherit;background:linear-gradient(90deg,var(--blue),var(--mint));transition:width .5s ease}.progress-copy{display:flex;justify-content:space-between;margin:10px 0 24px;color:var(--muted);font-size:13px}.checks{margin:0;padding:0;list-style:none;border-top:1px solid var(--line)}.check{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:14px 2px;border-bottom:1px solid var(--line)}.check-title{display:flex;align-items:center;gap:10px;font-size:14px;font-weight:650}.dot{width:22px;height:22px;display:grid;place-items:center;flex:0 0 auto;border-radius:50%;font-size:12px}.dot.ok{color:#087e68;background:#d9f8ef}.dot.wait{color:#9a5b00;background:#fff0cc}.badge{padding:5px 8px;border-radius:999px;font-size:11px;font-weight:750;letter-spacing:.05em}.badge.ok{color:#087e68;background:#d9f8ef}.badge.wait{color:#9a5b00;background:#fff0cc}.action{display:flex;gap:14px;align-items:center;margin-top:28px}.button{border:0;border-radius:11px;padding:13px 18px;color:white;cursor:pointer;font:inherit;font-weight:700;background:var(--navy)}.button:disabled{cursor:wait;opacity:.65}.action-note{color:var(--muted);font-size:13px;line-height:1.45}.running,.error{display:none;margin-top:20px;padding:12px 14px;border-radius:10px;font-size:14px}.running{align-items:center;gap:10px;color:#0a5a8d;background:#e8f5ff}.error{color:#a52222;background:#ffebeb}.spinner{width:17px;height:17px;border:2px solid #8cc9ec;border-top-color:#1678b9;border-radius:50%;animation:spin .8s linear infinite}@@keyframes spin{to{transform:rotate(360deg)}}@@media(max-width:520px){.action{align-items:flex-start;flex-direction:column}.check{align-items:flex-start}.badge{margin-left:32px}}
</style>
</head>
<body>
@php
$checks = [
['Koneksi dan keamanan database', $status['pgcrypto']],
['Identitas petugas', $status['pegawai_columns']['id']],
['Informasi kontak petugas', $status['pegawai_columns']['email']],
['Verifikasi akun petugas', $status['pegawai_columns']['email_verified_at']],
['Keamanan sesi petugas', $status['pegawai_columns']['remember_token']],
['Akses masuk aplikasi', $status['users_view']],
['Riwayat verifikasi transaksi', $status['veri_time']],
['Penyimpanan sesi aplikasi', $status['sessions']],
];
$readyCount = count(array_filter(array_column($checks, 1)));
@endphp
<main class="shell"><div class="brand"><span class="brand-mark">P</span>P-Solution Parking</div><section class="panel"><h1>Menyiapkan aplikasi untuk digunakan.</h1><p class="lead">Kami sedang memeriksa kebutuhan awal aplikasi. Data dan pengaturan yang sudah ada tetap aman, lalu aplikasi akan menyesuaikan diri dengan sistem parkir di lokasi ini.</p><div class="progress"><div class="progress-bar" style="width:{{ (int) round(($readyCount / count($checks)) * 100) }}%"></div></div><div class="progress-copy"><span>Status persiapan</span><span>{{ $readyCount }}/{{ count($checks) }} bagian siap</span></div><ul class="checks">@foreach($checks as [$label, $ready])<li class="check"><span class="check-title"><span class="dot {{ $ready ? 'ok' : 'wait' }}">{{ $ready ? '✓' : '!' }}</span>{{ $label }}</span><span class="badge {{ $ready ? 'ok' : 'wait' }}">{{ $ready ? 'SIAP' : 'PERLU DISIAPKAN' }}</span></li>@endforeach</ul><div class="action"><button id="runSetup" class="button" type="button">Siapkan Aplikasi</button><span class="action-note">Proses ini tidak menghapus data transaksi atau data operasional yang sudah ada.</span></div><div id="running" class="running"><span class="spinner"></span><span id="runningText">Menyiapkan aplikasi...</span></div><div id="error" class="error"></div></section></main>
<script>
const button=document.getElementById('runSetup'),running=document.getElementById('running'),runningText=document.getElementById('runningText'),errorBox=document.getElementById('error');button.addEventListener('click',async()=>{button.disabled=true;button.textContent='Sedang menyiapkan...';running.style.display='flex';errorBox.style.display='none';const messages=['Memeriksa kesiapan sistem...','Menyesuaikan data petugas...','Menyiapkan akses aplikasi...','Menyelesaikan pengaturan awal...'];let index=0;const interval=setInterval(()=>runningText.textContent=messages[index++%messages.length],900);try{const response=await fetch('{{ route('setup.run') }}',{method:'POST',headers:{'X-CSRF-TOKEN':document.querySelector('meta[name="csrf-token"]').content,'Accept':'application/json'}});const result=await response.json();if(!response.ok||!result.success)throw new Error(result.message||'Persiapan aplikasi belum berhasil.');clearInterval(interval);runningText.textContent='Aplikasi siap. Mengalihkan ke halaman login...';setTimeout(()=>window.location.assign(result.redirect),800)}catch(error){clearInterval(interval);running.style.display='none';errorBox.textContent=error.message;errorBox.style.display='block';button.disabled=false;button.textContent='Coba Siapkan Lagi'}});
</script>
</body>
</html>
@@ -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>
@@ -0,0 +1,27 @@
<div
class="modal fade bd-details-modal-lg"
id="modal-details"
tabindex="-1"
role="dialog"
data-bs-backdrop="static"
data-keyboard="false"
>
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h4 class="card-title">Preview Member</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-danger light" data-bs-dismiss="modal">
Close
</button>
<button type="button" class="btn btn-outline-inverse-info btn-sm btn-icon-text">
Print
<i class="mdi mdi-printer btn-icon-append"></i>
</button>
</div>
</div>
</div>
</div>
+52 -35
View File
@@ -1,8 +1,8 @@
<div class="modal fade bd-extend-modal-lg" id="extend-member" tabindex="-1" role="dialog" data-bs-backdrop="static" data-keyboard="false">
<div class="modal fade bd-extend-modal-lg" id="modal-extend" tabindex="-1" role="dialog" data-bs-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h4 class="card-title">Ubah Data Stiker/Member</h4>
<h4 class="card-title">Perpanjang Member</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal">
</button>
</div>
@@ -10,106 +10,123 @@
<div class="custom-tab-1">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="tab" href="#home1"><i class="la la-user me-2"></i>Data Pengunjung</a>
<a class="nav-link active" data-bs-toggle="tab" href="#ext-data"><i class="la la-user me-2"></i>Data Pengunjung</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#profile1"><i class="la la-home me-2"></i> Data Kendaraan</a>
<a class="nav-link" data-bs-toggle="tab" href="#ext-vehicle"><i class="la la-home me-2"></i> Data Kendaraan</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#contact1"><i class="la la-phone me-2"></i>Data Akses</a>
<a class="nav-link" data-bs-toggle="tab" href="#ext-card"><i class="la la-phone me-2"></i>Data Akses</a>
</li>
</ul>
<form class="ubah-member" id="edit-member" methode="POST">
@csrf
@method('PUT')
<form class="extend-member" id="extend-member" action="{{ route('member.store')}}">
<div class="tab-content">
<div class="tab-pane fade show active" id="home1" role="tabpanel">
<div class="tab-pane fade show active" id="ext-data" role="tabpanel">
<div class="pt-4">
<div class="row">
<div class="col-xl-6">
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">No. Transaksi</label>
<div class="col-lg-8">
<input type="text" class="form-control form-control-sm notransnya" name="notrans" readonly>
<input type="hidden" class="form-control"readonly>
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Jenis Transaksi</label>
<div class="col-lg-8">
<input type="text" class="form-control" value="PERPANJANG" readonly>
<input type="text" class="form-control form-control-sm" value="PERPANJANG" readonly>
<input type="hidden" class="form-control" name="jenis_transaksi" value="1" readonly>
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label required">Nama</label>
<div class="col-lg-8">
<input type="text" class="form-control" name="nama" placeholder="Masuk kan nama...." required>
<input type="text" class="form-control form-control-sm namanya" name="nama" value="" required>
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Alamat</label>
<div class="col-lg-8">
<textarea type="text" class="form-control" rows="3" name="address" placeholder="JL. Hati hati raya..."></textarea>
<textarea type="text" class="form-control form-control-sm address" rows="3" name="address" value="" ></textarea>
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Telepon</label>
<div class="col-lg-8">
<input type="number" class="form-control" phone="phone" placeholder="+62 81/081...">
<input type="number" class="form-control form-control-sm" phone="phone" value="{{ old('phone')}}">
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label required">Periode :</label>
<div class="col-lg-8">
<input class="form-control input-daterange-timepicker" type="text" name="daterange" value="">
<input type="text" class="form-control form-control-sm input-daterange-timepicker-ext" id="periode-ext" name="periode" readonly>
</div>
</div>
</div>
<div class="col-xl-6">
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Jenis</label>
<label class="col-lg-3 col-form-label form-label">Jenis Langganan</label>
<div class="col-lg-6">
<select class="select-data form-control" name="member" id="member">
<select class="form-select" name="member" id="ext-jenisLang">
<option selected value="">Pilih Jenis</option>
@foreach ($memberSelect as $member)
<option value="{{ $member->id }}">{{ $member->nama }}</option>
@endforeach
</select>
<input type="hidden" name="member" id="ext-jenisLang-value">
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Dept</label>
<label class="col-lg-3 col-form-label form-label">Jenis Member</label>
<div class="col-lg-6">
<select class="select-data form-control" name="depart" id="department">
<select class="form-select" name="depart" id="ext-department">
<option selected value="">Pilih Departemen</option>
@foreach ($departmentSelect as $department)
<option value="{{ $department->kode }}">{{ $department->nama }}</option>
@endforeach
</select>
<input type="hidden" name="depart" id="ext-department-value">
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Note :</label>
<div class="col-lg-6">
<input type="text" class="form-control" name="note" placeholder="Catatan...">
<input type="text" class="form-control form-control-sm" name="note" placeholder="Catatan...">
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Harga (Rp.):</label>
<div class="col-lg-6">
<input type="text" class="form-control" name="harga" placeholder="10.000">
<input type="text" class="form-control form-control-sm harga" name="harga">
</div>
</div>
{{-- <div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label required">Best Skill</label>
<div class="col-lg-6">
<select class="default-select wide form-control" id="validationCustom05">
<option data-display="Select">Please select</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
</select>
</div>
</div> --}}
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="profile1">
<div class="tab-pane fade" id="ext-vehicle">
<div class="pt-4">
<h4>Tambahkan Kendaraan</h4>
<div class="row form-material">
<div class="col-xl-3 col-md-6 mb-3">
<label class="form-label">Nomer Polisi</label>
<input type="text" class="form-control nopol-add" onkeyup="this.value = this.value.toUpperCase();" placeholder="No. Polisi">
<input type="text" class="form-control form-control-sm ext-nopol-add" onkeyup="this.value = this.value.toUpperCase();" placeholder="No. Polisi">
<div class="invalid-feedback">Nomer Polisi sudah ada</div>
</div>
<div class="col-xl-3 col-md-6 mb-3">
<label class="form-label">Jenis Kendaraan :</label>
<select class="select form-control" id="jenisVehicle">
<select class="form-select" id="jenisVehicleExt">
<option selected value="">Jenis Kendaraan</option>
@foreach ($vehicleSelect as $kendaraan)
<option value="{{ $kendaraan->id }}">{{ $kendaraan->nama }}</option>
@@ -118,49 +135,49 @@
</div>
<div class="col-xl-3 col-md-6 mb-3">
<label class="form-label">Jenis Member :</label>
<select class="select-data form-control member" id="jenis-member">
<select class="form-select member" id="jenisMemberExt">
</select>
</div>
<div class="col-xl-3 col-md-6 mb-3">
<label class="form-label">Merk : </label>
<input type="text" class="form-control merk" onkeyup="this.value = this.value.toUpperCase();" placeholder="Honda...">
<input type="text" class="form-control form-control-sm merkExt" onkeyup="this.value = this.value.toUpperCase();" placeholder="Honda...">
</div>
<div class="col-xl-3 col-md-6 mb-3">
<label class="form-label"></label>
<button type="button" id="nopol-add" class="btn btn-primary btn-xs">Tambah</button>
<button type="button" id="ext-nopol-add" class="btn btn-primary btn-xs">Tambah</button>
</div>
<hr>
<table class="table-bordered table-striped">
<table class="">
<thead>
<tr>
<th class="col-2 text-center p-2">Nomer Polisi</th>
<th class="col-2 text-center">Jenis Kendaraan</th>
<th class="col-2 text-center">Merk Kendaraan</th>
<th class="col-2 text-center">Type Member</th>
<th class="col-2 text-center">Merk Kendaraan</th>
<th class="col-2 text-center">Aksi</th>
</tr>
</thead>
<tbody id="list-nopol">
<tbody id="list-nopol-ext">
</tbody>
</table>
</div>
</div>
</div>
<div class="tab-pane fade" id="contact1">
<div class="tab-pane fade" id="ext-card">
<div class="pt-4">
<h4>Tambahkan Kartu</h4>
<div class="row form-material">
<div class="col-xl-6 col-md-6 mb-3">
<label class="form-label">Nomer Kartu</label>
<input type="text" class="form-control kartu" placeholder="01234567...">
<input type="text" class="form-control form-control-sm kartu" placeholder="01234567...">
</div>
<div class="col-xl-6 col-md-6 mb-3">
<label class="form-label">Nomer Urut</label>
<input type="text" class="form-control nourut" placeholder="01234567...">
<input type="text" class="form-control form-control-sm nourut" placeholder="01234567...">
</div>
<hr>
{{-- <div class="table-responsive"> --}}
<table class="table-bordered">
<table class="table-basic">
<thead>
<tr>
<th class="col-5 text-center p-2" >Nomer Kartu</th>
@@ -168,7 +185,7 @@
<th class="col-2 text-center">Aksi</th>
</tr>
</thead>
<tbody id="list-card">
<tbody id="list-card-ext">
</tbody>
</table>
{{-- </div> --}}
@@ -181,7 +198,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger light" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary simpan">Simpan Perubahan</button>
<button type="button" class="btn btn-primary simpan-ext">Save changes</button>
</div>
</div>
</div>
+89 -359
View File
@@ -51,82 +51,90 @@
<div class="col-md-2 grid-margin stretch-card">
<div class="card shadow-sm h-100">
<div class="card-body">
<form id="search-form">
<h5 class="card-title mb-4">Metode Pencarian Transaksi</h5>
<h5 class="card-title mb-4">Metode Pencarian Transaksi</h5>
<!-- DASAR PENCARIAN -->
<div class="mb-3">
<label class="form-label fw-semibold">Dasar Pencarian</label>
<!-- DASAR PENCARIAN -->
<div class="mb-3">
<label class="form-label fw-semibold">Dasar Pencarian</label>
<select class="form-select cari-data mb-2" name="search-by" id="search-by">
<option selected value="">Pencarian Data... (Semua)</option>
<option value="nama">Nama</option>
<option value="alamat">Alamat</option>
<option value="nopol">No. Polisi</option>
<option value="nocard">No. Card</option>
<option value="notrans">No. Transaksi</option>
</select>
<select class="form-select mb-2" id="search-by">
<option selected value="">Pencarian Data... (Semua)</option>
<option value="nama">Nama</option>
<option value="alamat">Alamat</option>
<option value="nopol">No. Polisi</option>
<option value="nocard">No. Card</option>
<option value="notrans">No. Transaksi</option>
</select>
<input type="text" class="form-control form-control-sm" name="search_value" id="search-value" placeholder="Masukkan kata kunci...">
</div>
<input type="text" class="form-control" id="search-value" placeholder="Masukkan kata kunci...">
</div>
<hr class="my-3">
<hr class="my-3">
<!-- JENIS LANGGANAN -->
<div class="mb-3">
<label class="form-label fw-semibold">Jenis Langganan</label>
<select class="form-select" name="jenismember" id="member-type">
<option value="">Semua</option>
@foreach ($memberSelect as $jenisLangganan)
<option value="{{ $jenisLangganan->id }}">
{{ $jenisLangganan->nama }}
</option>
@endforeach
</select>
</div>
<!-- JENIS LANGGANAN -->
<div class="mb-3">
<label class="form-label fw-semibold">Jenis Langganan</label>
<select class="form-select" id="member-type">
<option value="">Semua</option>
@foreach ($memberSelect as $jenisLangganan)
<option value="{{ $jenisLangganan->id }}">
{{ $jenisLangganan->nama }}
</option>
@endforeach
</select>
</div>
<!-- UNIT KERJA -->
<div class="mb-3">
<label class="form-label fw-semibold">Unit Kerja</label>
<select class="form-select" name="depart" id="department">
<option value="">Semua</option>
@foreach ($departmentSelect as $jeniStiker)
<option value="{{ $jeniStiker->kode }}">
{{ $jeniStiker->nama }}
</option>
@endforeach
</select>
</div>
<!-- UNIT KERJA -->
<div class="mb-3">
<label class="form-label fw-semibold">Unit Kerja</label>
<select class="form-select" id="department">
<option value="">Semua</option>
@foreach ($departmentSelect as $jeniStiker)
<option value="{{ $jeniStiker->kode }}">
{{ $jeniStiker->nama }}
</option>
@endforeach
</select>
</div>
<!-- KENDARAAN -->
<div class="mb-3">
<label class="form-label fw-semibold">Kendaraan</label>
<select class="form-select" name="vehicle" id="vehicle">
<option value="">Semua</option>
@foreach ($vehicleSelect as $vehicle)
<option value="{{ $vehicle->id }}">
{{ $vehicle->nama }}
</option>
@endforeach
</select>
</div>
<!-- KENDARAAN -->
<div class="mb-3">
<label class="form-label fw-semibold">Kendaraan</label>
<select class="form-select" id="vehicle">
<option value="">Semua</option>
@foreach ($vehicleSelect as $vehicle)
<option value="{{ $vehicle->id }}">
{{ $vehicle->nama }}
</option>
@endforeach
</select>
</div>
<div class="mb-3 d-flex align-items-center justify-content-between">
<p class="mb-0">Member Aktif</p>
<label class="toggle-switch mb-0">
<input type="checkbox" name="isActive" id="isActive" checked>
<span class="toggle-slider round"></span>
</label>
</div>
<!-- KATEGORI -->
{{-- <div class="mb-4">
<label class="form-label fw-semibold">Kategori</label>
<input type="text"
class="form-control"
id="category"
placeholder="Masukkan kategori...">
</div> --}}
<!-- BUTTON -->
<div class="d-grid">
<button type="button" id="search-button" class="btn btn-primary">
<i class="fa fa-search me-2"></i> Cari Data
</button>
</div>
<!-- KATEGORI -->
{{-- <div class="mb-4">
<label class="form-label fw-semibold">Kategori</label>
<input type="text"
class="form-control"
id="category"
placeholder="Masukkan kategori...">
</div> --}}
<!-- BUTTON -->
<div class="d-grid">
<button type="button" id="search-button" class="btn btn-primary">
<i class="fa fa-search me-2"></i> Cari Data...
</button>
</div>
</form>
</div>
</div>
</div>
@@ -139,25 +147,25 @@ class="form-control"
<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-success btn-sm extend-button" id="btnExtend" data-bs-toggle="modal" data-bs-target=".bd-extend-modal-lg">Perpanjang</button>
<button class="btn btn-success btn-sm extend-button" id="btnExtend" data-bs-toggle="modal" data-bs-target="#modal-extend" data-id="${element.notrans}" data-url="{{ route('getStikerMember.show', ':id') }}" >Perpanjang</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-info btn-sm detail-button" id="btnDetail" data-bs-toggle="modal" data-bs-target="#modal-details">Detail</button>
<button class="btn btn-danger btn-sm" id="btnDelete">Hapus</button>
<button class="btn btn-info btn-sm" id="btnDetail">Detail</button>
</div>
</div>
<div class="table-responsive pt-3">
<table class="table table-bordered table-selectable" id="example" data-url="{{ route('getMember') }}">
<table class="table table-basic table-selectable" id="example" data-url="{{ route('getMember') }}">
<thead>
<tr>
<th class="p-3" style="text-align: center;">No.</th>
<th style="text-align: center;">No. Transaksi</th>
<th style="text-align: center;">Nama</th>
<th style="text-align: center;">Alamat</th>
<th class="p-3" style="text-align: center;" width="3%">No.</th>
<th style="text-align: center;" width="17%">No. Transaksi</th>
<th style="text-align: center;" width="8%">Nama</th>
<th style="text-align: center;" width="10%">Alamat</th>
<th style="text-align: center;">Telepone</th>
<th style="text-align: center;">Jenis Langganan</th>
<th style="text-align: center;">Departement</th>
<th style="text-align: center;">Periode</th>
<th style="text-align: center;">Details Kendaraan</th>
<th style="text-align: center;" width="11%">Periode</th>
<th style="text-align: center;" width="10%">Details Kendaraan</th>
<th style="text-align: center;">No. Kartu</th>
{{-- <th width="11%" style="text-align: center;">Action</th> --}}
</tr>
@@ -170,6 +178,7 @@ class="form-control"
@include('stiker.tambah-stiker')
@include('stiker.extend-stiker')
@include('stiker.ubah-stiker')
@include('stiker.details-stiker')
<!-- END MEMMBER MODAL -->
</div>
</div>
@@ -193,297 +202,18 @@ class="form-control"
<script src="{{asset ('vendor/sweetalert2/dist/sweetalert2.min.js') }}"></script>
<script src="{{asset ('js/custom.min.js') }}"></script>
<script src="{{asset ('js/dlabnav-init.js') }}"></script>
<script src="{{asset ('js/tooltips.js') }}"></script>
<script>
var member = @json($jenisStiker);
let selectedRowData = null;
</script>
@if(app()->environment('local'))
<script src="{{ asset('js/stikerM ember/index.js') }}"></script>
<script src="{{ asset('js/stikerMember/index.js') }}"></script>
<script src="{{ asset('js/stikerMember/member-register.js') }}"></script>
<script src="{{ asset('js/stikerMember/member-extend.js') }}"></script>
<script src="{{ asset('js/stikerMember/member-details.js') }}"></script>
@else
<script src="{{asset ('jx/stikermember/index.parkq.jx') }}"></script>
<script src="{{asset ('jx/stikermember/member-register.parkq.jx') }}"></script>
@endif
<script>
$(document).ready(function () {
var vehicle = @json($vehicleSelect);
let selectedRowData = null;
$('#example tbody').on('click', 'tr', function () {
console.log($('#example tbody tr').length);
const notrans = $(this).data('notrans');
// hapus semua highlight
$('#example tbody tr').removeClass('selected');
// highlight semua row dalam 1 grup
$(`#example tbody tr[data-notrans="${notrans}"]`).addClass('selected');
// ambil data dari row pertama grup
const firstRow = $(`#example tbody tr[data-notrans="${notrans}"]`).first();
selectedRowData = {
notrans : firstRow.data('notrans')
};
console.log('Selected transaksi:', selectedRowData);
$('#rowActions').removeClass('d-none');
});
$(document).on('click', '.extend-button', function () {
console.log("Tombol extend ditekan");
if (!selectedRowData) {
alert('Pilih data terlebih dahulu');
return;
}
var rawId = $(this).data('id');
// let id = rawId.substring(0, rawId.indexOf('/'));
let id = selectedRowData.notrans;
var kendaraan = vehicle;
console.log('Data ID : ', id);
var actionUrl = '{{ route("getStikerMember.show", ":id") }}';
actionUrl = actionUrl.replace(':id', "" + encodeURIComponent(id));
console.log('Action URL : ', actionUrl);
// $('#extend-member').attr('action'), actionUrl;
$.ajax({
url: actionUrl,
type: 'GET',
success: function (r) {
console.log("Datanya : ", r);
if (r) {
$('#extend-member').val('');
$('#list-nopol-ext').empty();
$('#list-card-ext').empty();
$('#notransnya').val(r.notrans);
$('#namanya').val(r.nama);
$('#address').val(r.alamat);
$('#periode').val(`${moment(r.awal).format('DD-MM-YYYY HH:mm')} - ${moment(r.akhir).format('DD-MM-YYYY HH:mm')}`);
$('#member-ext').val(r.unit_kerja);
$('#department-ext').val(r.jenis_langganan);
var rows = r.stiker_details;
if (rows.length !== 0) {
rows.forEach(row => {
var vehicleName = vehicle.find(v => v.id === row.jenis_mobil)?.nama || 'Unknown';
$('#list-nopol-ext').append(`
<tr>
<td class="text-center p-2">${row.nopol}</td>
<td class="text-center p-2" data-value="${row.jenis_mobil}">${vehicleName}</td>
<td class="text-center p-2">${row.merk}</td>
<td class="text-center p-2">${row.jenis_member}</td>
</tr>
`);
});
} else {
$('#list-nopol-ext').append(`
<tr>
<td class="text-center p-2">Tidak ada data</td>
<td class="text-center p-2">Tidak ada data</td>
<td class="text-center p-2">Tidak ada data</td>
<td class="text-center p-2">Tidak ada data</td>
</tr>
`);
}
var rowss = r.member_card;
if (rowss.length !== 0) {
rowss.forEach(ro => {
$('#list-card-ext').append(`
<tr>
<td class="text-center p-2">${ro.no_card}</td>
<td class="text-center p-2">${ro.no_urut}</td>
</tr>
`);
});
} else {
$('#list-card-ext').append(`
<tr>
<td class="text-center p-2">Tidak ada data</td>
<td class="text-center p-2">Tidak ada data</td>
</tr>
`);
}
console.log('Data : ', r);
}
},
error: function (xhr) {
console.error('Error : ', xhr);
},
});
$('#extend-member').modal('show');
});
$('.extend').on('click', function () {
// $('#modal-member-extend').modal('show');
var url = $('#extend-member').attr('action').replace(":notrans",$("#extend-member").data('notrans'));
var extend = $(this);
var formExtend = $('#extend-member').serialize();
var nopolData = [];
var cardData = [];
$('#list-nopol-ext tr').each(function () {
var row = $(this);
var rowData = {
nopol: row.find('td:nth-child(1)').text().trim(),
vehicle: row.find('td:nth-child(2)').data('value'),
member: row.find('td:nth-child(3)').text().trim(),
merk: row.find('td:nth-child(4)').text().trim(),
};
nopolData.push(rowData);
});
console.log(nopolData);
$('#list-card-ext tr').each(function () {
var row = $(this);
var rowCard = {
nocard: row.find('td:nth-child(1)').text(),
nourut: row.find('td:nth-child(2)').text(),
};
cardData.push(rowCard);
});
const stringDataExt = formExtend;
// Mengonversi query string menjadi objek
const formDataExt = {};
const searchExtend = new URLSearchParams(stringDataExt);
searchExtend.forEach((value, key) => {
formDataExt[key] = value;
});
// console.log(formDataExt);
var extData = {
formData: formDataExt,
nopolData: nopolData,
cardNumber: cardData,
};
$.ajax({
url: url,
type: 'POST',
data: extData,
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
berforeSend: function () {
extend.text('Loading...');
},
success: function (data) {
if (data.response === true) {
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: data.message,
});
$('#modal-member-extend').modal('hide');
} else {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: data.message,
});
}
console.log(data);
},
error: function (xhr) {
console.error('Error : ', xhr);
},
});
});
$(document).on('click', '.edit-button', function () {
let id = selectedRowData;
$('#edit-member').attr('action', '/transaksi/member/' + id);
$('#ubah-member').modal('show');
// console.log(selectedRowData);
// console.log(id);
$.get(`/transaksi/member/${selectedRowData}`, function(res){
fillMemberModal(res);
$('#modal-member').modal('show');
});
});
// $('#btnEdit').on('click', function(){
// if(!selectedRowData) {
// alert('Pilih data dulu');
// return;
// }
// console.log(selectedRowData);
// $.get(`/member/${selectedRowData.notrans}`, function(res){
// fillMemberModal(res);
// $('#modal-member').modal('show');
// });
// });
function fillMemberModal(res){
let m = res.member;
// TAB 1 - DATA PENGUNJUNG
$('[name="nama"]').val(m.nama);
$('[name="address"]').val(m.alamat);
$('[name="daterange"]').val(m.awal + ' - ' + m.akhir);
$('[name="member"]').val(m.jenis_langganan).trigger('change');
$('[name="depart"]').val(m.dept).trigger('change');
$('[name="note"]').val(m.note);
$('[name="harga"]').val(m.harga);
// =========================
// TAB 2 - KENDARAAN
// =========================
$('#list-nopol').empty();
res.kendaraan.forEach(v => {
$('#list-nopol').append(`
<tr>
<td>${v.nopol}</td>
<td>${v.jenis}</td>
<td>${v.merk}</td>
<td>${v.member}</td>
<td>
<button class="btn btn-danger btn-sm remove-nopol" data-id="${v.id}">Hapus</button>
</td>
</tr>
`);
});
// =========================
// TAB 3 - KARTU
// =========================
$('#list-card').empty();
res.kartu.forEach(k => {
$('#list-card').append(`
<tr>
<td>${k.kartu}</td>
<td>${k.nourut}</td>
<td>
<button class="btn btn-danger btn-sm remove-card" data-id="${k.id}">Hapus</button>
</td>
</tr>
`);
});
}
$('#modal-member').on('hidden.bs.modal', function () {
$(this).find('form')[0].reset();
$('#list-nopol').empty();
$('#list-card').empty();
});
});
</script>
@endsection
+20 -13
View File
@@ -25,35 +25,42 @@
<div class="pt-4">
<div class="row">
<div class="col-xl-6">
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">No. Transaksi</label>
<div class="col-lg-8">
<input type="text" class="form-control form-control-sm" value="OTOMATIS" readonly>
<input type="hidden" class="form-control form-control-sm"readonly>
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Jenis Transaksi</label>
<div class="col-lg-8">
<input type="text" class="form-control" value="BARU" readonly>
<input type="hidden" class="form-control" name="jenis_transaksi" value="0" readonly>
<input type="text" class="form-control form-control-sm" value="BARU" readonly>
<input type="hidden" class="form-control form-control-sm" name="jenis_transaksi" value="0" readonly>
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label required">Nama</label>
<div class="col-lg-8">
<input type="text" class="form-control" name="nama" placeholder="Masuk kan nama...." required>
<input type="text" class="form-control form-control-sm" name="nama" placeholder="Masuk kan nama...." required>
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Alamat</label>
<div class="col-lg-8">
<textarea type="text" class="form-control" rows="3" name="address" placeholder="JL. Hati hati raya..."></textarea>
<textarea type="text" class="form-control form-control-sm" rows="3" name="address" placeholder="JL. Hati hati raya..."></textarea>
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Telepon</label>
<div class="col-lg-8">
<input type="number" class="form-control" phone="phone" placeholder="+62 81/081...">
<input type="text" class="form-control form-control-sm" phone="phone" placeholder="+62 81/081...">
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label required">Periode :</label>
<div class="col-lg-8">
<input type="text" class="form-control input-daterange-timepicker" name="periode" readonly>
<input type="text" class="form-control form-control-sm input-daterange-timepicker" name="periode" readonly>
</div>
</div>
</div>
@@ -83,13 +90,13 @@
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Note :</label>
<div class="col-lg-6">
<input type="text" class="form-control" name="note" placeholder="Catatan...">
<input type="text" class="form-control form-control-sm" name="note" placeholder="Catatan...">
</div>
</div>
<div class="mb-3 row">
<label class="col-lg-3 col-form-label form-label">Harga (Rp.):</label>
<div class="col-lg-6">
<input type="text" class="form-control" name="harga" placeholder="10.000">
<input type="text" class="form-control form-control-sm" name="harga" placeholder="10.000">
</div>
</div>
{{-- <div class="mb-3 row">
@@ -112,7 +119,7 @@
<div class="row form-material">
<div class="col-xl-3 col-md-6 mb-3">
<label class="form-label">Nomer Polisi</label>
<input type="text" class="form-control nopol-add" onkeyup="this.value = this.value.toUpperCase();" placeholder="No. Polisi">
<input type="text" class="form-control form-control-sm nopol-add" onkeyup="this.value = this.value.toUpperCase();" placeholder="No. Polisi">
<div class="invalid-feedback">Nomer Polisi sudah ada</div>
</div>
<div class="col-xl-3 col-md-6 mb-3">
@@ -131,7 +138,7 @@
</div>
<div class="col-xl-3 col-md-6 mb-3">
<label class="form-label">Merk : </label>
<input type="text" class="form-control merk" onkeyup="this.value = this.value.toUpperCase();" placeholder="Honda...">
<input type="text" class="form-control form-control-sm merk" onkeyup="this.value = this.value.toUpperCase();" placeholder="Honda...">
</div>
<div class="col-xl-3 col-md-6 mb-3">
<label class="form-label"></label>
@@ -143,8 +150,8 @@
<tr>
<th class="col-2 text-center p-2">Nomer Polisi</th>
<th class="col-2 text-center">Jenis Kendaraan</th>
<th class="col-2 text-center">Merk Kendaraan</th>
<th class="col-2 text-center">Type Member</th>
<th class="col-2 text-center">Merk Kendaraan</th>
<th class="col-2 text-center">Aksi</th>
</tr>
</thead>
@@ -160,11 +167,11 @@
<div class="row form-material">
<div class="col-xl-6 col-md-6 mb-3">
<label class="form-label">Nomer Kartu</label>
<input type="text" class="form-control kartu" placeholder="01234567...">
<input type="text" class="form-control form-control-sm kartu" placeholder="01234567...">
</div>
<div class="col-xl-6 col-md-6 mb-3">
<label class="form-label">Nomer Urut</label>
<input type="text" class="form-control nourut" placeholder="01234567...">
<input type="text" class="form-control form-control-sm nourut" placeholder="01234567...">
</div>
<hr>
{{-- <div class="table-responsive"> --}}
+6 -6
View File
@@ -10,20 +10,20 @@
<div class="custom-tab-1">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="tab" href="#home1"><i class="la la-user me-2"></i>Data Pengunjung</a>
<a class="nav-link active" data-bs-toggle="tab" href="#edit-data"><i class="la la-user me-2"></i>Data Pengunjung</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#profile1"><i class="la la-home me-2"></i> Data Kendaraan</a>
<a class="nav-link" data-bs-toggle="tab" href="#edit-vehicle"><i class="la la-home me-2"></i> Data Kendaraan</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="tab" href="#contact1"><i class="la la-phone me-2"></i>Data Akses</a>
<a class="nav-link" data-bs-toggle="tab" href="#edit-card"><i class="la la-phone me-2"></i>Data Akses</a>
</li>
</ul>
<form class="ubah-member" id="edit-member" methode="POST">
@csrf
@method('PUT')
<div class="tab-content">
<div class="tab-pane fade show active" id="home1" role="tabpanel">
<div class="tab-pane fade show active" id="edit-data" role="tabpanel">
<div class="pt-4">
<div class="row">
<div class="col-xl-6">
@@ -98,7 +98,7 @@
</div>
</div>
</div>
<div class="tab-pane fade" id="profile1">
<div class="tab-pane fade" id="edit-vehicle">
<div class="pt-4">
<h4>Tambahkan Kendaraan</h4>
<div class="row form-material">
@@ -146,7 +146,7 @@
</div>
</div>
</div>
<div class="tab-pane fade" id="contact1">
<div class="tab-pane fade" id="edit-card">
<div class="pt-4">
<h4>Tambahkan Kartu</h4>
<div class="row form-material">
+31 -140
View File
@@ -1,148 +1,39 @@
<!DOCTYPE html>
<html>
<html lang="id">
<head>
<title>Laravel Setup</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>P-Solution Parking</title>
<style>
/* body { font-family: sans-serif; padding: 30px }
.ok { color: green }
.fail { color: red }
button { padding: 10px 20px } */
body { font-family: sans-serif; padding: 30px }
.ok { color: green }
.fail { color: red }
button {
padding: 10px 20px;
cursor: pointer;
}
#loader {
display: none;
margin-top: 20px;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #ddd;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
:root { --navy:#102a43; --blue:#1f6feb; --mint:#2dd4bf; --muted:#64748b; }
* { box-sizing:border-box; }
body { min-height:100vh; margin:0; color:var(--navy); font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; background:radial-gradient(circle at 15% 15%,#d7f6f0 0,transparent 32%),linear-gradient(135deg,#f7fbff,#eaf1f8); }
main { width:min(960px,calc(100% - 40px)); min-height:100vh; display:grid; grid-template-columns:1.1fr .9fr; gap:48px; align-items:center; margin:auto; }
.brand { display:flex; gap:12px; align-items:center; font-size:14px; font-weight:800; letter-spacing:.05em; text-transform:uppercase; }.mark { display:grid; width:38px; height:38px; place-items:center; color:#fff; border-radius:12px; background:linear-gradient(135deg,#2877e8,#153f83); box-shadow:0 10px 25px #1f6feb38; }
h1 { margin:26px 0 16px; font-size:clamp(38px,6vw,62px); letter-spacing:-.055em; line-height:1.02; }.copy { max-width:520px; color:var(--muted); font-size:17px; line-height:1.7; }
.login { display:inline-flex; gap:10px; align-items:center; margin-top:28px; padding:14px 19px; border-radius:12px; color:#fff; text-decoration:none; font-weight:750; background:var(--navy); box-shadow:0 12px 25px #102a4328; }.login:hover { background:#163b60; }
.visual { position:relative; min-height:390px; overflow:hidden; border:1px solid #fff; border-radius:28px; background:linear-gradient(150deg,#173b65,#0e2743); box-shadow:0 25px 70px #102a4330; }.lane { position:absolute; width:190%; height:90px; left:-42%; transform:rotate(-33deg); background:repeating-linear-gradient(90deg,transparent 0 42px,#eaf4fb 42px 68px,transparent 68px 120px); opacity:.85; }.lane.one { top:105px; }.lane.two { top:250px; }.signal { position:absolute; top:52px; right:48px; width:86px; height:164px; padding:14px; border-radius:18px; background:#0b1d30; box-shadow:0 14px 28px #0005; }.signal span { display:block; width:40px; height:40px; margin:0 auto 10px; border-radius:50%; background:#25384c; }.signal span:last-child { background:#31d6a4; box-shadow:0 0 20px #31d6a4aa; }.card { position:absolute; left:32px; bottom:34px; right:32px; padding:20px; border:1px solid #ffffff24; border-radius:16px; color:#fff; background:#ffffff12; backdrop-filter:blur(8px); }.card small { color:#b5cde4; }.card strong { display:block; margin-top:7px; font-size:20px; }
@@media(max-width:760px) { main { grid-template-columns:1fr; gap:30px; padding:48px 0; }.visual { min-height:280px; }.lane.one { top:65px; }.lane.two { top:180px; } }
</style>
</head>
<body>
@if ($status['users_view'] )
<h2>🚀 Laravel Setup Checker</h2>
<h2>🚀 Laravel Setup Checker</h2>
<script>window.location = "/login";</script>
@else
@if(session('success'))
<p class="ok">{{ session('success') }}</p>
@endif
@if(session('error'))
<p class="fail">{{ session('error') }}</p>
@endif
<ul>
<li>pgcrypto:
<b class="{{ $status['pgcrypto'] ? 'ok' : 'fail' }}">
{{ $status['pgcrypto'] ? 'OK' : 'NOT FOUND' }}
</b>
</li>
<li>pegawai.id:
<b class="{{ $status['pegawai_columns']['id'] ? 'ok' : 'fail' }}">
{{ $status['pegawai_columns']['id'] ? 'OK' : 'MISSING' }}
</b>
</li>
<li>pegawai.email:
<b class="{{ $status['pegawai_columns']['email'] ? 'ok' : 'fail' }}">
{{ $status['pegawai_columns']['email'] ? 'OK' : 'MISSING' }}
</b>
</li>
<li>pegawai.email_verified_at:
<b class="{{ $status['pegawai_columns']['email_verified_at'] ? 'ok' : 'fail' }}">
{{ $status['pegawai_columns']['email_verified_at'] ? 'OK' : 'MISSING' }}
</b>
</li>
<li>pegawai.remember_token:
<b class="{{ $status['pegawai_columns']['remember_token'] ? 'ok' : 'fail' }}">
{{ $status['pegawai_columns']['remember_token'] ? 'OK' : 'MISSING' }}
</b>
</li>
<li>users view:
<b class="{{ $status['users_view'] ? 'ok' : 'fail' }}">
{{ $status['users_view'] ? 'OK' : 'NOT FOUND' }}
</b>
</li>
<li>transaksi_parkir.veri_time:
<b class="{{ $status['veri_time'] ? 'ok' : 'fail' }}">
{{ $status['veri_time'] ? 'OK' : 'MISSING' }}
</b>
</li>
</ul>
<form method="POST" action="{{ route('setup.run') }}">
@csrf
<button type="submit">⚙️ Jalankan Setup</button>
<div id="loader">
<div class="spinner"></div>
<b>Memproses setup database…</b>
</div>
</form>
@endif
<script>
document.getElementById('runSetup').addEventListener('click', function () {
const btn = this;
const loader = document.getElementById('loader');
btn.disabled = true;
btn.innerText = 'Processing...';
loader.style.display = 'block';
fetch("{{ route('setup.run') }}", {
method: "POST",
headers: {
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').content,
"Accept": "application/json"
}
})
.then(res => res.json())
.then(res => {
if (res.success) {
loader.innerHTML = "<b class='ok'>✅ Setup selesai, mengalihkan halaman...</b>";
setTimeout(() => {
window.location.href = res.redirect;
}, 1500);
}
})
.catch(err => {
loader.innerHTML = "<b class='fail'>❌ Setup gagal</b>";
btn.disabled = false;
btn.innerText = '⚙️ Jalankan Setup';
console.error(err);
});
});
</script>
<main>
<section>
<div class="brand"><span class="mark">P</span>P-Solution Parking</div>
<h1>Operasional parkir, siap dalam satu tempat.</h1>
<p class="copy">Kelola aktivitas parkir, transaksi, member, dan pengaturan lokasi melalui sistem operasional P-Solution Parking.</p>
@if($isReady)
<a class="login" href="{{ route('login') }}">Masuk ke Aplikasi <span>→</span></a>
@else
<p class="copy" style="font-size:14px;margin-top:24px">Aplikasi perlu disiapkan untuk database di lokasi ini sebelum dapat digunakan.</p>
<a class="login" href="{{ route('setup.index') }}">Siapkan Aplikasi <span>→</span></a>
@endif
</section>
<aside class="visual" aria-hidden="true">
<div class="lane one"></div><div class="lane two"></div>
<div class="signal"><span></span><span></span><span></span></div>
<div class="card"><small>SISTEM OPERASIONAL</small><strong>Parkir lebih terukur, layanan lebih lancar.</strong></div>
</aside>
</main>
</body>
</html>