447 lines
13 KiB
PHP
447 lines
13 KiB
PHP
@extends('app.app')
|
|
|
|
@section('title')
|
|
Conf Pools
|
|
@endsection
|
|
|
|
@section('customcss')
|
|
<style>
|
|
:root {
|
|
--badge-i-color: #0d6efd; /* Bootstrap Primary Blue */
|
|
--badge-f-color: #fd7e14; /* Bootstrap Orange */
|
|
}
|
|
|
|
body {
|
|
background-color: #f8f9fa;
|
|
}
|
|
|
|
.gallery-card {
|
|
transition: transform 0.2s ease-in-out, box-shadow 0.2s;
|
|
cursor: pointer;
|
|
overflow: hidden;
|
|
border: none;
|
|
}
|
|
|
|
.gallery-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
|
|
}
|
|
|
|
/* Image container to ensure consistent aspect ratio */
|
|
.img-container {
|
|
height: 150px;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
background-color: #e9ecef;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.img-container img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.gallery-card:hover .img-container img {
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
/* Custom Badge Styling - Updated for longer text */
|
|
.media-badge {
|
|
position: absolute;
|
|
top: 10px;
|
|
right: 10px;
|
|
/* Removed fixed width/height to accommodate text */
|
|
padding: 0.15rem 0.45rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
/* font-weight: bold; */
|
|
z-index: 2;
|
|
/* font-family: sans-serif; Changed to sans-serif for better readability */
|
|
font-size: 0.6rem;
|
|
text-transform: uppercase;
|
|
/* letter-spacing: 0.5px; */
|
|
border: 2px solid white;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
border-radius: 50rem; /* Pill shape */
|
|
color: white;
|
|
}
|
|
|
|
.badge-i {
|
|
background-color: var(--badge-i-color);
|
|
}
|
|
|
|
.badge-f {
|
|
background-color: var(--badge-f-color);
|
|
}
|
|
|
|
/* Timestamp Styling */
|
|
.timestamp-label {
|
|
font-size: 0.75rem;
|
|
color: #6c757d;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
}
|
|
|
|
/* Icon simulation for timestamp */
|
|
.clock-icon {
|
|
display: inline-block;
|
|
width: 12px;
|
|
height: 12px;
|
|
border: 1.5px solid #6c757d;
|
|
border-radius: 50%;
|
|
position: relative;
|
|
}
|
|
.clock-icon::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 4px;
|
|
height: 1px;
|
|
background: #6c757d;
|
|
transform-origin: left;
|
|
transform: rotate(-90deg);
|
|
}
|
|
|
|
.filter-btn.active {
|
|
background-color: #212529;
|
|
color: white;
|
|
border-color: #212529;
|
|
}
|
|
|
|
/* Utility to remove whitespace around inline-block wrapper */
|
|
.font-zero {
|
|
font-size: 0;
|
|
}
|
|
</style>
|
|
@endsection
|
|
|
|
@section('content')
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-sm-2 bg-white p-0 d-flex flex-column"
|
|
id="leftSidebar"
|
|
style="height: calc(100vh - 56px);">
|
|
|
|
<!-- Search -->
|
|
<div class="p-3 border-bottom flex-shrink-0">
|
|
<input type="text" id="filterVhcSearch" class="form-control form-control-sm" placeholder="Search Vehicle">
|
|
</div>
|
|
|
|
<!-- Scrollable list - this is the real fix -->
|
|
<div class="overflow-auto flex-fill">
|
|
<ul id="listVehicle" class="list-group list-group-flush m-0">
|
|
<!-- items -->
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="col p-2" style="height: calc(-56px + 100vh); position: relative;">
|
|
{{-- media content --}}
|
|
<div class="card card-body h-100">
|
|
<h5 class="card-title">Media Content <span id="nopol"></span></h5>
|
|
<div class="row">
|
|
<div class="col-2">
|
|
<div class="form-group">
|
|
<label class="text-muted">From</label>
|
|
<!-- default today -->
|
|
<!-- <input class="form-control" id="tgl0" value="02-09-2025 00:00"> -->
|
|
<input class="form-control" id="tgl0" value="{{ date('d-m-Y 00:00') }}">
|
|
</div>
|
|
</div>
|
|
<div class="col-2">
|
|
<div class="form-group">
|
|
<label class="text-muted">To</label>
|
|
<!-- <input class="form-control" id="tgl1" value="02-09-2025 23:00"> -->
|
|
<input class="form-control" id="tgl1" value="{{ date('d-m-Y 23:59') }}">
|
|
</div>
|
|
</div>
|
|
<div class="col-2">
|
|
<div class="form-group">
|
|
<label class="text-muted">Camera</label>
|
|
<select name="license_plate" class="form-control" id="filterCamera">
|
|
<option value="">-- All --</option>
|
|
<option value="F">Front</option>
|
|
<option value="I">Inside</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-1 d-flex align-items-end">
|
|
<button class="btn btn-primary" id="submitFilter">Submit</button>
|
|
</div>
|
|
</div>
|
|
<hr class="mb-2">
|
|
{{-- media list --}}
|
|
<!-- Gallery Grid -->
|
|
<div class="row g-3 overflow-auto mt-0" id="listMedia">
|
|
<!-- <div class="col-sm-6 col-md-4 col-lg-3 gallery-item" data-type="I">
|
|
<div class="card h-100 gallery-card shadow-sm">
|
|
<span class="media-badge badge-i">Inside</span>
|
|
<div class="img-container">
|
|
<img src="https://images.unsplash.com/photo-1682687220742-aba13b6e50ba?auto=format&fit=crop&w=500&q=60" alt="Mountain">
|
|
</div>
|
|
<div class="card-body bg-white border-top">
|
|
<h6 class="card-title text-truncate">Living Room 001</h6>
|
|
<div class="timestamp-label mt-2">
|
|
<span class="clock-icon"></span> 2023-10-24 09:30 AM
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Image Modal -->
|
|
<div class="modal fade" id="imageModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content bg-transparent border-0 shadow-none">
|
|
<div class="modal-body p-0 text-center position-relative">
|
|
<button type="button" class="btn-close btn-close-white position-absolute top-0 end-0 m-3 z-3" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
<img src="" id="modalImage" class="img-fluid rounded shadow-lg" style="max-height: 90vh;" alt="Preview">
|
|
</div>
|
|
<!-- <div class="modal-body p-0 text-center">
|
|
<div class="d-inline-block position-relative font-zero">
|
|
<button type="button" class="btn-close btn-close-white position-absolute top-0 end-0 m-2 z-3" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
<img src="" id="modalImage" class="img-fluid rounded shadow-lg" style="max-height: 90vh;" alt="Preview">
|
|
</div>
|
|
</div> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@endsection
|
|
|
|
@section('customjs')
|
|
<script>
|
|
'use strict';
|
|
|
|
let STATE = {
|
|
vhcId: null,
|
|
defaultEmptyMedia: `
|
|
<div class="col text-center text-muted">
|
|
No media available
|
|
</div>
|
|
`
|
|
}
|
|
|
|
const Wrapper = {
|
|
activate: function() {
|
|
Wrapper.event();
|
|
Wrapper.init();
|
|
},
|
|
init: function() {
|
|
$('#listMedia').html(STATE.defaultEmptyMedia);
|
|
|
|
// datetimepicker
|
|
$('#tgl0, #tgl1').datetimepicker({
|
|
format:'d-m-Y H:i',
|
|
defaultTime:'00:00',
|
|
closeOnDateSelect: true,
|
|
// mask:true
|
|
});
|
|
|
|
// vehicle list
|
|
$.ajax({
|
|
url: "{{ route('api_get_media', '') }}/",
|
|
data: {
|
|
a:'listVehicle'
|
|
},
|
|
method: 'GET',
|
|
crossDomain: true,
|
|
processData: true,
|
|
headers: {
|
|
'x-api-key': Helper.getCookie('_trtk'),
|
|
},
|
|
// data: params,
|
|
success: (data, textStatus, jqXHR) => {
|
|
// console.log("res data", data);
|
|
|
|
if (data.meta.type != 'success') {
|
|
resolve({
|
|
type: 'fail'
|
|
});
|
|
Helper.toast('Warning', 'just now', data.meta.message);
|
|
return false;
|
|
}
|
|
|
|
const listVehicle = data.data;
|
|
listVehicle.forEach(vhc => {
|
|
const vehicleItem = `
|
|
<li class="list-group-item vehicles-list-wrapper">
|
|
<a href="#" class="text-dark vhcItem" data-id="${vhc.vid}" data-nopol="${vhc.nopol1}">
|
|
<div class="row d-flex align-items-center">
|
|
<div class="col-3">
|
|
<img src="{{ asset('storage') }}/${vhc.fvhc_img}"
|
|
class="img-fluid" alt="">
|
|
</div>
|
|
<div class="col ps-0">
|
|
<p class="text-bold mb-0">${vhc.nopol1}</p>
|
|
<p class="text-muted mb-0">${vhc.name}</p>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
`
|
|
$('#listVehicle').append(vehicleItem);
|
|
});
|
|
},
|
|
error: (jqXHR, textStatus, error) => {
|
|
if (jqXHR.status >= 500) {
|
|
Helper.toast('Error', 'just now', 'please try again');
|
|
} else {
|
|
Helper.toast('Error', 'just now', jqXHR.responseJSON.meta
|
|
.message);
|
|
}
|
|
resolve({
|
|
type: 'error'
|
|
});
|
|
}
|
|
})
|
|
},
|
|
event: function() {
|
|
// filter vehicle list
|
|
$('#filterVhcSearch').on('keyup', function() {
|
|
const filterValue = $(this).val().toLowerCase();
|
|
$('#listVehicle .vehicles-list-wrapper').filter(function() {
|
|
$(this).toggle($(this).text().toLowerCase().indexOf(filterValue) > -1)
|
|
});
|
|
});
|
|
|
|
// vehicle list item click
|
|
$('#listVehicle').on('click', '.vhcItem', function() {
|
|
STATE.vhcId = $(this).data('id');
|
|
const nopol = $(this).data('nopol');
|
|
$('#nopol').text(nopol);
|
|
|
|
// get media
|
|
Wrapper.f.getMedia();
|
|
});
|
|
|
|
// submit filter
|
|
$('#submitFilter').on('click', function() {
|
|
// get media
|
|
Wrapper.f.getMedia();
|
|
});
|
|
|
|
// image modal
|
|
$('#listMedia').on('click', '.gallery-item', function() {
|
|
// const src = $(this).data('src');
|
|
// $('#modalImage').attr('src', src);
|
|
// $('#imageModal').modal('show');
|
|
|
|
const imgUrl = $(this).find('img').attr('src');
|
|
const bigImgUrl = imgUrl.replace('w=500', 'w=1200');
|
|
$('#modalImage').attr('src', bigImgUrl);
|
|
$('#imageModal').modal('show');
|
|
});
|
|
// $('.gallery-card').click(function() {
|
|
// // Get the image source from the clicked card
|
|
// const imgUrl = $(this).find('img').attr('src');
|
|
// const bigImgUrl = imgUrl.replace('w=500', 'w=1200');
|
|
// $('#modalImage').attr('src', bigImgUrl);
|
|
|
|
// // Upgrade resolution for the modal (replace w=500 with w=1200 for better quality)
|
|
|
|
// // Set the modal image source
|
|
|
|
// // Show the modal using Bootstrap's jQuery interface
|
|
// $('#imageModal').modal('show');
|
|
// });
|
|
|
|
},
|
|
f:{
|
|
getMedia: () => {
|
|
if (!STATE.vhcId) {
|
|
Helper.toast('Warning', 'just now', 'please select a vehicle first');
|
|
return false;
|
|
}
|
|
|
|
const tgl0 = moment($('#tgl0').val(), "DD-MM-YYYY HH:mm").unix();
|
|
const tgl1 = moment($('#tgl1').val(), "DD-MM-YYYY HH:mm").unix();
|
|
const camera = $('#filterCamera').val();
|
|
|
|
$.ajax({
|
|
url: "{{ route('api_get_media', '') }}/",
|
|
data: {
|
|
a:'listMedia',
|
|
vhcId: STATE.vhcId,
|
|
tgl0: tgl0,
|
|
tgl1: tgl1,
|
|
camera: camera,
|
|
},
|
|
method: 'GET',
|
|
crossDomain: true,
|
|
processData: true,
|
|
headers: {
|
|
'x-api-key': Helper.getCookie('_trtk'),
|
|
},
|
|
// data: params,
|
|
success: (data, textStatus, jqXHR) => {
|
|
// console.log("res data", data);
|
|
|
|
if (data.meta.type != 'success') {
|
|
resolve({
|
|
type: 'fail'
|
|
});
|
|
Helper.toast('Warning', 'just now', data.meta.message);
|
|
return false;
|
|
}
|
|
|
|
$('#listMedia').html(''); // clear media list
|
|
if (data.count == 0) {
|
|
// no media
|
|
$('#listMedia').html(STATE.defaultEmptyMedia);
|
|
return false;
|
|
}
|
|
|
|
const listMedia = data.data;
|
|
listMedia.forEach(media => {
|
|
const mediaItem = `
|
|
<div class="col-sm-6 col-md-4 col-lg-2 gallery-item" data-type="${media.cam}">
|
|
<div class="card gallery-card shadow-sm">
|
|
<span class="media-badge badge-${media.cam.toLowerCase()}">${media.cam == 'I' ? 'Inside' : 'Front'}</span>
|
|
<div class="img-container">
|
|
<img src="${media.image}" alt="Media Image">
|
|
</div>
|
|
<div class="bg-white border-top p-2">
|
|
<div class="timestamp-label">
|
|
<span class="clock-icon"></span> ${moment.unix(media.crt_d/1000).format('DD MMM YYYY HH:mm:ss')}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
$('#listMedia').append(mediaItem);
|
|
});
|
|
},
|
|
error: (jqXHR, textStatus, error) => {
|
|
if (jqXHR.status >= 500) {
|
|
Helper.toast('Error', 'just now', 'please try again');
|
|
} else {
|
|
Helper.toast('Error', 'just now', jqXHR.responseJSON.meta
|
|
.message);
|
|
}
|
|
resolve({
|
|
type: 'error'
|
|
});
|
|
}
|
|
|
|
})
|
|
|
|
}}
|
|
};
|
|
|
|
|
|
|
|
Wrapper.activate();
|
|
</script>
|
|
@endsection
|