Files
gps-frontend/resources/views/menu_v1/media.blade.php
Pringgosutono bfb9fa8dc5 media menu
2025-11-21 13:52:46 +07:00

435 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;
}
</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 modal-xl">
<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>
</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.id}" 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', imgUrl);
$('#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