up file upload limit to 20mb and fix switch camera

This commit is contained in:
2025-07-14 13:00:59 +07:00
parent a59f685d41
commit 96a9729a35
4 changed files with 186 additions and 58 deletions

View File

@@ -34,16 +34,16 @@ class PostchecksController extends Controller
'pressure_high' => 'required|numeric|min:0',
'pressure_low' => 'nullable|numeric|min:0',
'cabin_temperature' => 'nullable|numeric',
'cabin_temperature_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'cabin_temperature_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
'ac_condition' => 'nullable|in:' . implode(',', Postcheck::getAcConditionOptions()),
'ac_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'ac_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
'blower_condition' => 'nullable|in:' . implode(',', Postcheck::getBlowerConditionOptions()),
'blower_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'blower_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
'evaporator_condition' => 'nullable|in:' . implode(',', Postcheck::getEvaporatorConditionOptions()),
'evaporator_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'evaporator_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
'compressor_condition' => 'nullable|in:' . implode(',', Postcheck::getCompressorConditionOptions()),
'postcheck_notes' => 'nullable|string',
'front_image' => 'required|image|mimes:jpeg,png,jpg|max:2048',
'front_image' => 'required|image|mimes:jpeg,png,jpg|max:20480',
]);
$data = [

View File

@@ -34,16 +34,16 @@ class PrechecksController extends Controller
'pressure_high' => 'required|numeric|min:0',
'pressure_low' => 'nullable|numeric|min:0',
'cabin_temperature' => 'nullable|numeric',
'cabin_temperature_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'cabin_temperature_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
'ac_condition' => 'nullable|in:' . implode(',', Precheck::getAcConditionOptions()),
'ac_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'ac_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
'blower_condition' => 'nullable|in:' . implode(',', Precheck::getBlowerConditionOptions()),
'blower_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'blower_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
'evaporator_condition' => 'nullable|in:' . implode(',', Precheck::getEvaporatorConditionOptions()),
'evaporator_image' => 'nullable|image|mimes:jpeg,png,jpg|max:2048',
'evaporator_image' => 'nullable|image|mimes:jpeg,png,jpg|max:20480',
'compressor_condition' => 'nullable|in:' . implode(',', Precheck::getCompressorConditionOptions()),
'precheck_notes' => 'nullable|string',
'front_image' => 'required|image|mimes:jpeg,png,jpg|max:2048',
'front_image' => 'required|image|mimes:jpeg,png,jpg|max:20480',
]);
$data = [

View File

@@ -420,7 +420,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -460,7 +460,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -507,7 +507,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -554,7 +554,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -601,7 +601,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -1086,13 +1086,7 @@
}
try {
// Stop current stream
stopCamera(videoId);
// Wait a bit before starting new camera
await new Promise(resolve => setTimeout(resolve, 100));
// Get current facing mode
// Get current facing mode before stopping
const currentTracks = streams[videoId] ? streams[videoId].getVideoTracks() : [];
let currentFacingMode = 'environment'; // default to back camera
@@ -1106,6 +1100,17 @@
// Switch to opposite camera
const newFacingMode = currentFacingMode === 'environment' ? 'user' : 'environment';
// Stop current stream
stopCamera(videoId);
// Wait a bit before starting new camera
await new Promise(resolve => setTimeout(resolve, 200));
// Try to get the specific camera
let stream;
try {
// First try with specific facing mode
const constraints = {
video: {
width: { min: 320, ideal: 640, max: 1280 },
@@ -1115,7 +1120,55 @@
}
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
stream = await navigator.mediaDevices.getUserMedia(constraints);
} catch (err) {
console.log('Gagal dengan facingMode, mencoba dengan deviceId...');
// If facingMode fails, try to get available devices and switch
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(device => device.kind === 'videoinput');
if (videoDevices.length > 1) {
// Find current device index
const currentDevice = videoDevices.find(device => {
if (currentTracks.length > 0) {
return device.deviceId === currentTracks[0].getSettings().deviceId;
}
return false;
});
const currentIndex = currentDevice ? videoDevices.indexOf(currentDevice) : 0;
const nextIndex = (currentIndex + 1) % videoDevices.length;
const nextDevice = videoDevices[nextIndex];
console.log('Beralih dari device:', currentDevice?.label, 'ke:', nextDevice?.label);
const deviceConstraints = {
video: {
deviceId: { exact: nextDevice.deviceId },
width: { min: 320, ideal: 640, max: 1280 },
height: { min: 240, ideal: 480, max: 720 },
aspectRatio: { ideal: 4/3 }
}
};
stream = await navigator.mediaDevices.getUserMedia(deviceConstraints);
} else {
// Fallback to basic constraints
stream = await navigator.mediaDevices.getUserMedia({
video: {
width: { min: 320, ideal: 640, max: 1280 },
height: { min: 240, ideal: 480, max: 720 },
aspectRatio: { ideal: 4/3 }
}
});
}
} catch (deviceErr) {
console.error('Gagal beralih dengan deviceId:', deviceErr);
throw deviceErr;
}
}
video.srcObject = stream;
streams[videoId] = stream;
@@ -1142,6 +1195,17 @@
console.log('Berhasil beralih ke kamera:', newFacingMode === 'environment' ? 'belakang' : 'depan');
// Show success notification
Swal.fire({
icon: 'success',
title: 'Kamera Berhasil Diganti',
text: `Berhasil beralih ke ${newFacingMode === 'environment' ? 'kamera belakang' : 'kamera depan'}`,
timer: 1500,
showConfirmButton: false,
toast: true,
position: 'top-end'
});
} catch (err) {
console.error('Gagal beralih kamera:', err);
Swal.fire({
@@ -1204,12 +1268,12 @@
return;
}
// Validasi ukuran file (max 2MB)
if (file.size > 2 * 1024 * 1024) {
// Validasi ukuran file (max 20MB)
if (file.size > 20 * 1024 * 1024) {
Swal.fire({
icon: 'warning',
title: 'Ukuran File Terlalu Besar',
text: 'Ukuran file maksimal 2MB',
text: 'Ukuran file maksimal 20MB',
confirmButtonText: 'OK'
});
return;

View File

@@ -420,7 +420,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -460,7 +460,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -507,7 +507,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -554,7 +554,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -601,7 +601,7 @@
</button>
</div>
<div class="mt-2">
<small class="text-muted">Atau upload foto dari galeri:</small>
<small class="text-muted">Atau upload foto dari galeri (maksimal 20MB):</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>
@@ -1086,13 +1086,7 @@
}
try {
// Stop current stream
stopCamera(videoId);
// Wait a bit before starting new camera
await new Promise(resolve => setTimeout(resolve, 100));
// Get current facing mode
// Get current facing mode before stopping
const currentTracks = streams[videoId] ? streams[videoId].getVideoTracks() : [];
let currentFacingMode = 'environment'; // default to back camera
@@ -1106,6 +1100,17 @@
// Switch to opposite camera
const newFacingMode = currentFacingMode === 'environment' ? 'user' : 'environment';
// Stop current stream
stopCamera(videoId);
// Wait a bit before starting new camera
await new Promise(resolve => setTimeout(resolve, 200));
// Try to get the specific camera
let stream;
try {
// First try with specific facing mode
const constraints = {
video: {
width: { min: 320, ideal: 640, max: 1280 },
@@ -1115,7 +1120,55 @@
}
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
stream = await navigator.mediaDevices.getUserMedia(constraints);
} catch (err) {
console.log('Gagal dengan facingMode, mencoba dengan deviceId...');
// If facingMode fails, try to get available devices and switch
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const videoDevices = devices.filter(device => device.kind === 'videoinput');
if (videoDevices.length > 1) {
// Find current device index
const currentDevice = videoDevices.find(device => {
if (currentTracks.length > 0) {
return device.deviceId === currentTracks[0].getSettings().deviceId;
}
return false;
});
const currentIndex = currentDevice ? videoDevices.indexOf(currentDevice) : 0;
const nextIndex = (currentIndex + 1) % videoDevices.length;
const nextDevice = videoDevices[nextIndex];
console.log('Beralih dari device:', currentDevice?.label, 'ke:', nextDevice?.label);
const deviceConstraints = {
video: {
deviceId: { exact: nextDevice.deviceId },
width: { min: 320, ideal: 640, max: 1280 },
height: { min: 240, ideal: 480, max: 720 },
aspectRatio: { ideal: 4/3 }
}
};
stream = await navigator.mediaDevices.getUserMedia(deviceConstraints);
} else {
// Fallback to basic constraints
stream = await navigator.mediaDevices.getUserMedia({
video: {
width: { min: 320, ideal: 640, max: 1280 },
height: { min: 240, ideal: 480, max: 720 },
aspectRatio: { ideal: 4/3 }
}
});
}
} catch (deviceErr) {
console.error('Gagal beralih dengan deviceId:', deviceErr);
throw deviceErr;
}
}
video.srcObject = stream;
streams[videoId] = stream;
@@ -1142,6 +1195,17 @@
console.log('Berhasil beralih ke kamera:', newFacingMode === 'environment' ? 'belakang' : 'depan');
// Show success notification
Swal.fire({
icon: 'success',
title: 'Kamera Berhasil Diganti',
text: `Berhasil beralih ke ${newFacingMode === 'environment' ? 'kamera belakang' : 'kamera depan'}`,
timer: 1500,
showConfirmButton: false,
toast: true,
position: 'top-end'
});
} catch (err) {
console.error('Gagal beralih kamera:', err);
Swal.fire({
@@ -1204,12 +1268,12 @@
return;
}
// Validasi ukuran file (max 2MB)
if (file.size > 2 * 1024 * 1024) {
// Validasi ukuran file (max 20MB)
if (file.size > 20 * 1024 * 1024) {
Swal.fire({
icon: 'warning',
title: 'Ukuran File Terlalu Besar',
text: 'Ukuran file maksimal 2MB',
text: 'Ukuran file maksimal 20MB',
confirmButtonText: 'OK'
});
return;