Files
CKB/resources/views/transaction/postchecks.blade.php

811 lines
28 KiB
PHP

@extends('layouts.frontapp')
@section('styles')
<style>
.card {
border: 1px solid #ddd;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-body {
padding: 20px;
}
.section-header {
background: linear-gradient(135deg, #2c5aa0 0%, #1e3a8a 100%);
color: white;
padding: 12px 20px;
border-radius: 8px;
margin: 25px 0 15px 0;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.section-header h5 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.section-header i {
margin-right: 8px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
font-weight: 600;
color: #333;
margin-bottom: 8px;
display: block;
}
.form-control {
border: 2px solid #e9ecef;
border-radius: 8px;
padding: 12px 15px;
font-size: 14px;
transition: all 0.3s ease;
}
.form-control:focus {
border-color: #2c5aa0;
box-shadow: 0 0 0 0.2rem rgba(44, 90, 160, 0.25);
}
.form-control[readonly] {
background-color: #f8f9fa;
color: #6c757d;
}
.camera-container {
border: 2px dashed #ddd;
border-radius: 12px;
padding: 15px;
background: #f8f9fa;
margin-top: 10px;
}
.camera-video {
width: 100%;
max-width: 400px;
height: 250px;
background: #000;
border-radius: 8px;
margin-bottom: 10px;
}
.camera-controls {
display: flex;
gap: 10px;
flex-wrap: wrap;
margin-bottom: 10px;
}
.btn {
border-radius: 8px;
font-weight: 500;
padding: 8px 16px;
font-size: 14px;
transition: all 0.3s ease;
}
.btn-primary {
background: linear-gradient(135deg, #2c5aa0 0%, #1e3a8a 100%);
border: none;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.btn-success {
background: linear-gradient(135deg, #059669 0%, #10b981 100%);
border: none;
}
.btn-success:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.btn-secondary {
background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
border: none;
}
.btn-secondary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.btn-lg {
padding: 15px 30px;
font-size: 16px;
}
.photo-preview {
margin-top: 10px;
}
.photo-preview img {
max-width: 200px;
max-height: 150px;
border-radius: 8px;
border: 3px solid #059669;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.alert {
border-radius: 8px;
border: none;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
#postcheck_notes {
resize: vertical;
min-height: 80px;
}
.file-input-hidden {
display: none;
}
.file-info {
margin-top: 5px;
font-size: 12px;
color: #6c757d;
}
/* Mobile Responsive */
@media (max-width: 768px) {
.container {
padding: 0 10px;
}
.card-body {
padding: 15px;
}
.section-header {
padding: 10px 15px;
margin: 20px 0 10px 0;
}
.section-header h5 {
font-size: 14px;
}
.form-control {
padding: 10px 12px;
font-size: 16px; /* Prevent zoom on iOS */
}
.camera-video {
height: 200px;
}
.camera-controls {
flex-direction: column;
}
.btn {
width: 100%;
margin-bottom: 5px;
}
.photo-preview img {
max-width: 100%;
height: auto;
}
.form-row {
margin: 0;
}
.form-row > .col-md-6 {
padding: 0 5px;
}
}
@media (max-width: 576px) {
.mobile-container {
padding: 0;
}
.card {
border-radius: 0;
margin-bottom: 0;
}
.camera-container {
padding: 10px;
}
.camera-video {
height: 180px;
}
}
</style>
@endsection
@section('content')
<div class="mobile-container">
<div class="container">
<div class="row mb-4 mt-4">
<div class="col-8">
<a href="/"><img src="{{ asset('logo-ckb.png') }}" style="width: 100%" alt="LOGO CKB"></a>
</div>
<div class="col-4 text-right my-auto">
<a class="btn btn-sm btn-danger mt-3" style="background: red !important;" href="{{ route('logout') }}" onclick="logout(event)">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<h2 class="text-center">Form Postcheck</h2>
</div>
</div>
@if(session('success'))
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('success') }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
@endif
@if($errors->any())
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<ul class="mb-0">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
@endif
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<a href="/" class="btn btn-warning btn-sm">
Kembali
</a>
<form action="{{ route('postchecks.store', $transaction->id) }}" method="POST" id="postcheckForm" enctype="multipart/form-data">
@csrf
<input type="hidden" name="transaction_id" value="{{ $transaction->id }}">
<!-- Informasi Transaksi -->
<div class="section-header">
<h5><i class="fas fa-info-circle"></i> Informasi Transaksi</h5>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label>Nomor Polisi</label>
<input type="text" class="form-control" value="{{ $transaction->police_number }}" readonly>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Nomor SPK</label>
<input type="text" class="form-control" value="{{ $transaction->spk }}" readonly>
</div>
</div>
</div>
<!-- Data Dasar -->
<div class="section-header">
<h5><i class="fas fa-clipboard-list"></i> Data Dasar</h5>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label>Tanggal Postcheck</label>
<input type="text" class="form-control" value="{{ now()->format('d/m/Y H:i') }}" readonly>
<small class="form-text text-muted">Tanggal dan waktu saat ini akan digunakan</small>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="kilometer">Kilometer <span class="text-danger">*</span></label>
<input type="number" class="form-control" id="kilometer" name="kilometer" step="0.01" required>
</div>
</div>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label for="pressure_high">Pressure High (PSI) <span class="text-danger">*</span></label>
<input type="number" class="form-control" id="pressure_high" name="pressure_high" step="0.01" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="pressure_low">Pressure Low (PSI)</label>
<input type="number" class="form-control" id="pressure_low" name="pressure_low" step="0.01">
</div>
</div>
</div>
<!-- Foto Depan -->
<div class="section-header">
<h5><i class="fas fa-camera"></i> Foto Depan Kendaraan <span class="text-danger">*</span></h5>
</div>
<div class="form-group">
<input type="file" id="front_image" name="front_image" accept="image/*" class="file-input-hidden" required>
<div class="camera-container">
<video id="front_camera" autoplay playsinline class="camera-video"></video>
<canvas id="front_canvas" style="display: none;"></canvas>
<div class="camera-controls">
<button type="button" class="btn btn-primary btn-sm" onclick="startCamera('front_camera')">
<i class="fas fa-camera"></i> Buka Kamera
</button>
<button type="button" class="btn btn-success btn-sm" onclick="capturePhoto('front_camera', 'front_canvas', 'front_image', 'front_preview')">
<i class="fas fa-camera-retro"></i> Ambil Foto
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<input type="file" class="form-control-file mt-1" accept="image/*" onchange="handleFileUpload(this, 'front_image', 'front_preview')">
</div>
<div id="front_preview" class="photo-preview"></div>
</div>
</div>
<!-- Foto Suhu Kabin -->
<div class="section-header">
<h5><i class="fas fa-thermometer-half"></i> Foto Suhu Kabin</h5>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label for="cabin_temperature">Suhu Kabin (°C)</label>
<input type="number" class="form-control" id="cabin_temperature" name="cabin_temperature" step="0.1" placeholder="Masukkan suhu kabin">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="cabin_temperature_image">Foto Suhu Kabin</label>
<input type="file" id="cabin_temperature_image" name="cabin_temperature_image" accept="image/*" class="file-input-hidden">
<div class="camera-container">
<video id="cabin_camera" autoplay playsinline class="camera-video"></video>
<canvas id="cabin_canvas" style="display: none;"></canvas>
<div class="camera-controls">
<button type="button" class="btn btn-primary btn-sm" onclick="startCamera('cabin_camera')">
<i class="fas fa-camera"></i> Buka Kamera
</button>
<button type="button" class="btn btn-success btn-sm" onclick="capturePhoto('cabin_camera', 'cabin_canvas', 'cabin_temperature_image', 'cabin_preview')">
<i class="fas fa-camera-retro"></i> Ambil Foto
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<input type="file" class="form-control-file mt-1" accept="image/*" onchange="handleFileUpload(this, 'cabin_temperature_image', 'cabin_preview')">
</div>
<div id="cabin_preview" class="photo-preview"></div>
</div>
</div>
</div>
</div>
<!-- Kondisi AC -->
<div class="section-header">
<h5><i class="fas fa-snowflake"></i> Kondisi AC</h5>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label for="ac_condition">Kondisi AC</label>
<select class="form-control" id="ac_condition" name="ac_condition">
<option value="">Pilih Kondisi</option>
@foreach($acConditions as $condition)
<option value="{{ $condition }}">{{ ucfirst($condition) }}</option>
@endforeach
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="ac_image">Foto AC</label>
<input type="file" id="ac_image" name="ac_image" accept="image/*" class="file-input-hidden">
<div class="camera-container">
<video id="ac_camera" autoplay playsinline class="camera-video"></video>
<canvas id="ac_canvas" style="display: none;"></canvas>
<div class="camera-controls">
<button type="button" class="btn btn-primary btn-sm" onclick="startCamera('ac_camera')">
<i class="fas fa-camera"></i> Buka Kamera
</button>
<button type="button" class="btn btn-success btn-sm" onclick="capturePhoto('ac_camera', 'ac_canvas', 'ac_image', 'ac_preview')">
<i class="fas fa-camera-retro"></i> Ambil Foto
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<input type="file" class="form-control-file mt-1" accept="image/*" onchange="handleFileUpload(this, 'ac_image', 'ac_preview')">
</div>
<div id="ac_preview" class="photo-preview"></div>
</div>
</div>
</div>
</div>
<!-- Kondisi Blower -->
<div class="section-header">
<h5><i class="fas fa-fan"></i> Kondisi Blower</h5>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label for="blower_condition">Kondisi Blower</label>
<select class="form-control" id="blower_condition" name="blower_condition">
<option value="">Pilih Kondisi</option>
@foreach($blowerConditions as $condition)
<option value="{{ $condition }}">{{ ucfirst($condition) }}</option>
@endforeach
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="blower_image">Foto Blower</label>
<input type="file" id="blower_image" name="blower_image" accept="image/*" class="file-input-hidden">
<div class="camera-container">
<video id="blower_camera" autoplay playsinline class="camera-video"></video>
<canvas id="blower_canvas" style="display: none;"></canvas>
<div class="camera-controls">
<button type="button" class="btn btn-primary btn-sm" onclick="startCamera('blower_camera')">
<i class="fas fa-camera"></i> Buka Kamera
</button>
<button type="button" class="btn btn-success btn-sm" onclick="capturePhoto('blower_camera', 'blower_canvas', 'blower_image', 'blower_preview')">
<i class="fas fa-camera-retro"></i> Ambil Foto
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<input type="file" class="form-control-file mt-1" accept="image/*" onchange="handleFileUpload(this, 'blower_image', 'blower_preview')">
</div>
<div id="blower_preview" class="photo-preview"></div>
</div>
</div>
</div>
</div>
<!-- Kondisi Evaporator -->
<div class="section-header">
<h5><i class="fas fa-tint"></i> Kondisi Evaporator</h5>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label for="evaporator_condition">Kondisi Evaporator</label>
<select class="form-control" id="evaporator_condition" name="evaporator_condition">
<option value="">Pilih Kondisi</option>
@foreach($evaporatorConditions as $condition)
<option value="{{ $condition }}">{{ ucfirst($condition) }}</option>
@endforeach
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="evaporator_image">Foto Evaporator</label>
<input type="file" id="evaporator_image" name="evaporator_image" accept="image/*" class="file-input-hidden">
<div class="camera-container">
<video id="evaporator_camera" autoplay playsinline class="camera-video"></video>
<canvas id="evaporator_canvas" style="display: none;"></canvas>
<div class="camera-controls">
<button type="button" class="btn btn-primary btn-sm" onclick="startCamera('evaporator_camera')">
<i class="fas fa-camera"></i> Buka Kamera
</button>
<button type="button" class="btn btn-success btn-sm" onclick="capturePhoto('evaporator_camera', 'evaporator_canvas', 'evaporator_image', 'evaporator_preview')">
<i class="fas fa-camera-retro"></i> Ambil Foto
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<input type="file" class="form-control-file mt-1" accept="image/*" onchange="handleFileUpload(this, 'evaporator_image', 'evaporator_preview')">
</div>
<div id="evaporator_preview" class="photo-preview"></div>
</div>
</div>
</div>
</div>
<!-- Kondisi Compressor -->
<div class="section-header">
<h5><i class="fas fa-cogs"></i> Kondisi Compressor</h5>
</div>
<div class="form-row">
<div class="col-md-6">
<div class="form-group">
<label for="compressor_condition">Kondisi Compressor</label>
<select class="form-control" id="compressor_condition" name="compressor_condition">
<option value="">Pilih Kondisi</option>
@foreach($compressorConditions as $condition)
<option value="{{ $condition }}">{{ ucfirst($condition) }}</option>
@endforeach
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="postcheck_notes">Catatan Tambahan</label>
<textarea class="form-control" id="postcheck_notes" name="postcheck_notes" rows="3" placeholder="Masukkan catatan tambahan jika ada..."></textarea>
</div>
</div>
</div>
<!-- Tombol Submit -->
<div class="section-header">
<h5><i class="fas fa-save"></i> Simpan Data</h5>
</div>
<div class="form-group text-center">
<button type="submit" class="btn btn-primary btn-lg">
Simpan Postcheck
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@section('javascripts')
<script>
let streams = {};
// Logout function
function logout(event){
event.preventDefault();
Swal.fire({
title: 'Logout?',
text: "Anda akan keluar dari sistem!",
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#dedede',
confirmButtonText: 'Logout'
}).then((result) => {
if (result.value) {
$('#logout-form').submit();
}
})
}
// Fallback untuk browser lama
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function(constraints) {
// Coba berbagai versi getUserMedia
const getUserMedia = navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia ||
navigator.oGetUserMedia;
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia tidak didukung di browser ini'));
}
return new Promise(function(resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
}
}
// Tambahan fallback untuk browser yang sangat lama
if (navigator.getUserMedia === undefined) {
navigator.getUserMedia = navigator.mediaDevices.getUserMedia;
}
// Start camera
async function startCamera(videoId) {
try {
const video = document.getElementById(videoId);
// Cek apakah browser mendukung getUserMedia
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
throw new Error('Browser tidak mendukung akses kamera');
}
// Stop stream yang sedang berjalan
if (streams[videoId]) {
streams[videoId].getTracks().forEach(track => track.stop());
}
// Konfigurasi kamera
const constraints = {
video: {
width: { min: 320, ideal: 640, max: 1280 },
height: { min: 240, ideal: 480, max: 720 },
aspectRatio: { ideal: 4/3 }
}
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
video.srcObject = stream;
streams[videoId] = stream;
// Tunggu video siap
video.onloadedmetadata = function() {
video.play();
};
video.onerror = function(e) {
console.error('Error pada video:', e);
alert('Error pada video stream');
};
} catch (err) {
// Pesan error yang lebih spesifik
let errorMessage = 'Tidak dapat mengakses kamera. ';
if (err.name === 'NotAllowedError') {
errorMessage += 'Izin kamera ditolak. Silakan:\n1. Klik ikon kamera di address bar\n2. Pilih "Allow"\n3. Refresh halaman';
} else if (err.name === 'NotFoundError') {
errorMessage += 'Kamera tidak ditemukan. Pastikan HP memiliki kamera.';
} else if (err.name === 'NotReadableError') {
errorMessage += 'Kamera sedang digunakan aplikasi lain. Tutup aplikasi kamera lain.';
} else if (err.name === 'OverconstrainedError') {
errorMessage += 'Kamera tidak mendukung resolusi yang diminta.';
} else if (err.name === 'SecurityError') {
errorMessage += 'Akses kamera diblokir. Pastikan menggunakan HTTPS atau localhost.';
} else {
errorMessage += 'Error: ' + err.message;
}
alert(errorMessage);
}
}
// Capture photo and convert to file
function capturePhoto(videoId, canvasId, inputId, previewId) {
const video = document.getElementById(videoId);
const canvas = document.getElementById(canvasId);
const fileInput = document.getElementById(inputId);
const preview = document.getElementById(previewId);
if (!video.srcObject) {
alert('Silakan buka kamera terlebih dahulu');
return;
}
// Pastikan video sudah siap
if (video.videoWidth === 0 || video.videoHeight === 0) {
alert('Video belum siap. Tunggu sebentar dan coba lagi.');
return;
}
try {
const context = canvas.getContext('2d');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// Convert canvas ke File object
canvas.toBlob(function(blob) {
// Buat File object
const file = new File([blob], `photo_${Date.now()}.jpg`, {
type: 'image/jpeg',
lastModified: Date.now()
});
// Assign ke file input
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
// Preview
const url = URL.createObjectURL(blob);
preview.innerHTML = `
<img src="${url}" style="max-width: 200px; max-height: 150px; border-radius: 8px; border: 3px solid #059669; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<div class="mt-2">
<small class="text-success"><i class="fas fa-check"></i> Foto berhasil diambil</small>
<br>
<small class="text-muted">Ukuran: ${(file.size / 1024).toFixed(1)} KB</small>
</div>
`;
}, 'image/jpeg', 0.8);
} catch (err) {
alert('Gagal mengambil foto: ' + err.message);
}
}
// Handle file upload from gallery
function handleFileUpload(input, inputId, previewId) {
const file = input.files[0];
if (!file) return;
if (!file.type.startsWith('image/')) {
alert('Pilih file gambar');
return;
}
// Validasi ukuran file (max 2MB)
if (file.size > 2 * 1024 * 1024) {
alert('Ukuran file maksimal 2MB');
return;
}
// Assign ke file input yang sesuai
const targetInput = document.getElementById(inputId);
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
targetInput.files = dataTransfer.files;
// Preview
const url = URL.createObjectURL(file);
const preview = document.getElementById(previewId);
preview.innerHTML = `
<img src="${url}" style="max-width: 200px; max-height: 150px; border-radius: 8px; border: 3px solid #059669; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
<div class="mt-2">
<small class="text-success"><i class="fas fa-check"></i> Foto berhasil diupload</small>
<br>
<small class="text-muted">Ukuran: ${(file.size / 1024).toFixed(1)} KB</small>
</div>
`;
}
// Stop all cameras when page is unloaded
window.addEventListener('beforeunload', function() {
Object.values(streams).forEach(stream => {
stream.getTracks().forEach(track => track.stop());
});
});
// Form validation
document.getElementById('postcheckForm').addEventListener('submit', function(e) {
const requiredFields = ['kilometer', 'front_image', 'pressure_high'];
let isValid = true;
requiredFields.forEach(fieldId => {
const field = document.getElementById(fieldId);
if (field.type === 'file') {
// Validasi file input
if (!field.files || field.files.length === 0) {
field.classList.add('is-invalid');
isValid = false;
} else {
field.classList.remove('is-invalid');
}
} else {
// Validasi input biasa
if (!field.value.trim()) {
field.classList.add('is-invalid');
isValid = false;
} else {
field.classList.remove('is-invalid');
}
}
});
if (!isValid) {
e.preventDefault();
alert('Mohon lengkapi semua field yang wajib diisi');
}
});
</script>
@endsection