Initial Commit

This commit is contained in:
2025-05-06 15:05:09 +07:00
commit 7b885d7d45
695 changed files with 119779 additions and 0 deletions

View File

@@ -0,0 +1,760 @@
@push('scripts')
<script>
const style = document.createElement('style');
style.textContent = `
.draggable-text {
z-index: 1000;
}
.draggable-text:hover {
outline: 1px dashed #666;
}
`;
document.head.appendChild(style);
document.addEventListener('DOMContentLoaded', function() {
const editor = new UniversalCameraEditor();
const cameraButtons = document.querySelectorAll('#btnCamera');
const modal = document.getElementById('cameraModal');
const closeModal = document.getElementById('closeModal');
// Current input field to update
let currentInputField = null;
// Handle camera button click
cameraButtons.forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
const inputContainer = this.closest('.input-group');
currentInputField = inputContainer.querySelector('input[type="file"]');
modal.classList.remove('hidden');
modal.classList.add('modal-open');
editor.startCamera();
});
});
closeModal.addEventListener('click', function() {
modal.classList.add('hidden');
editor.closeCamera();
});
// Override save function
editor.saveAndUpload = async function() {
try {
// Create final canvas
const finalCanvas = document.createElement('canvas');
finalCanvas.width = this.canvas.width;
finalCanvas.height = this.canvas.height;
const ctx = finalCanvas.getContext('2d');
// Draw original photo and edited result on final canvas
ctx.drawImage(this.canvas, 0, 0);
ctx.drawImage(this.drawingCanvas, 0, 0);
// Convert canvas to Blob and create file
finalCanvas.toBlob(async (blob) => {
const file = new File([blob], `camera_photo_${Date.now()}.jpg`, {
type: "image/jpeg"
});
// Create FileList and update input field
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
if (currentInputField) {
currentInputField.files = dataTransfer.files;
const event = new Event('change', {
bubbles: true
});
currentInputField.dispatchEvent(event);
const previewContainer = currentInputField.closest('.input-group')
.querySelector('.preview-container');
if (previewContainer) {
const img = document.createElement('img');
img.src = URL.createObjectURL(file);
img.className = 'w-full h-32 object-cover rounded-lg';
previewContainer.innerHTML = '';
previewContainer.appendChild(img);
}
}
// Close modal
modal.classList.remove('show');
modal.style.display = 'none';
modal.setAttribute('aria-hidden', 'true');
// document.body.classList.remove('modal-open');
// Remove modal backdrop if exists
const backdrop = document.querySelector('.modal-backdrop');
if (backdrop) {
backdrop.remove();
}
// Stop camera stream
if (editor.stream) {
editor.stream.getTracks().forEach(track => track.stop());
editor.stream = null;
}
// Reset camera to initial state
editor.closeCamera();
// Reset scroll if needed
window.scrollTo(0, 0); // Adjust as necessary
}, 'image/jpeg', 0.8);
} catch (error) {
console.error('Error saving photo:', error);
alert('Error saving photo. Please try again.');
}
};
// Handle escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
const modal = document.getElementById('cameraModal');
if (modal.classList.contains('modal-open')) {
modal.classList.remove('modal-open');
modal.classList.add('hidden');
if (editor.stream) {
editor.stream.getTracks().forEach(track => track.stop());
}
}
}
});
});
</script>
<script>
class UniversalCameraEditor {
constructor() {
// Initialize elements
this.video = document.getElementById('video');
this.canvas = document.getElementById('canvas');
this.drawingCanvas = document.getElementById('drawingCanvas');
this.ctx = this.drawingCanvas.getContext('2d');
// Buttons
this.startButton = document.getElementById('startButton');
this.takePhotoButton = document.getElementById('takePhotoButton');
this.switchButton = document.getElementById('switchButton');
this.backButton = document.getElementById('backButton');
this.saveButton = document.getElementById('saveButton');
this.undoButton = document.getElementById('undoButton');
this.resetButton = document.getElementById('resetButton');
// Drawing controls
this.colorPicker = document.getElementById('colorPicker');
this.brushSize = document.getElementById('brushSize');
this.brushSizeValue = document.getElementById('brushSizeValue');
// Text modal elements
this.textModal = document.getElementById('textModal');
this.textInput = document.getElementById('textInput');
this.confirmTextBtn = document.getElementById('confirmTextBtn');
this.cancelTextBtn = document.getElementById('cancelTextBtn');
// Initialize state
this.stream = null;
this.currentFacingMode = 'environment';
this.isDrawing = false;
this.currentTool = 'brush';
this.drawingHistory = [];
this.historyIndex = -1;
// Drawing coordinates
this.startX = 0;
this.startY = 0;
this.lastX = 0;
this.lastY = 0;
// Setup
this.setupCanvas();
this.setupEventListeners();
this.setupDrawingContext();
// Add text overlay container
this.textOverlay = document.getElementById('textOverlay');
this.activeTextElement = null;
this.isDraggingText = false;
this.textOffset = {
x: 0,
y: 0
};
// Current input field reference
this.currentInputField = null;
}
setupCanvas() {
// Set initial canvas size
this.canvas.width = 1280;
this.canvas.height = (this.canvas.width * 3) / 4;
this.drawingCanvas.width = this.canvas.width;
this.drawingCanvas.height = this.canvas.height;
}
setupDrawingContext() {
this.ctx.strokeStyle = this.colorPicker.value;
this.ctx.lineWidth = this.brushSize.value;
this.ctx.lineCap = 'round';
this.ctx.lineJoin = 'round';
}
async startCamera() {
try {
if (this.stream) {
this.stream.getTracks().forEach(track => track.stop());
}
const constraints = {
video: {
facingMode: this.currentFacingMode,
width: {
ideal: 1280
},
height: {
ideal: 960
}
},
audio: false
};
this.stream = await navigator.mediaDevices.getUserMedia(constraints);
this.video.srcObject = this.stream;
// Enable buttons after camera starts
this.takePhotoButton.disabled = false;
this.switchButton.disabled = false;
// Show video, hide canvas
this.video.style.display = 'block';
this.canvas.style.display = 'none';
this.drawingCanvas.style.display = 'none';
// Hide editor controls
document.getElementById('editorControls').classList.add('hidden');
document.getElementById('cameraControls').classList.remove('hidden');
} catch (error) {
console.error('Error accessing camera:', error);
alert('Error accessing camera. Please make sure you have granted camera permissions.');
}
}
takePhoto() {
// Draw video frame to canvas
const context = this.canvas.getContext('2d');
context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
// Hide video, show canvases
this.video.style.display = 'none';
this.canvas.style.display = 'block';
this.drawingCanvas.style.display = 'block';
// Show editor controls, hide camera controls
document.getElementById('editorControls').classList.remove('hidden');
document.getElementById('cameraControls').classList.add('hidden');
// Clear drawing history
this.drawingHistory = [];
this.historyIndex = -1;
this.saveDrawingState();
}
takePhoto() {
const context = this.canvas.getContext('2d');
// Determine if the video is in portrait or landscape mode
const isPortrait = this.video.videoHeight > this.video.videoWidth;
if (isPortrait) {
// Portrait mode
this.canvas.width = this.video.videoWidth;
this.canvas.height = this.video.videoHeight;
context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
} else {
// Landscape mode
this.canvas.width = 1280;
this.canvas.height = (this.canvas.width * this.video.videoHeight) / this.video.videoWidth;
context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
}
// Adjust drawing canvas to match main canvas
this.drawingCanvas.width = this.canvas.width;
this.drawingCanvas.height = this.canvas.height;
// Hide video, show canvases
this.video.style.display = 'none';
this.canvas.style.display = 'block';
this.drawingCanvas.style.display = 'block';
// Show editor controls, hide camera controls
document.getElementById('editorControls').classList.remove('hidden');
document.getElementById('cameraControls').classList.add('hidden');
// Clear drawing history
this.drawingHistory = [];
this.historyIndex = -1;
this.saveDrawingState();
}
switchCamera() {
this.currentFacingMode = this.currentFacingMode === 'environment' ? 'user' : 'environment';
this.startCamera();
}
resetToCamera() {
// Clear canvases
const context = this.canvas.getContext('2d');
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
// Reset history
this.drawingHistory = [];
this.historyIndex = -1;
// Restart camera
this.startCamera();
}
saveDrawingState() {
// Trim history if we're not at the end
if (this.historyIndex < this.drawingHistory.length - 1) {
this.drawingHistory = this.drawingHistory.slice(0, this.historyIndex + 1);
}
// Save current state
this.drawingHistory.push(this.drawingCanvas.toDataURL());
this.historyIndex++;
}
undo() {
if (this.historyIndex > 0) {
this.historyIndex--;
this.redrawHistory();
}
}
resetDrawing() {
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
this.saveDrawingState();
}
// Implement all event listeners from previous code here
setupEventListeners() {
// Camera controls
this.startButton.onclick = () => this.startCamera();
this.takePhotoButton.onclick = () => this.takePhoto();
this.switchButton.onclick = () => this.switchCamera();
this.backButton.onclick = () => this.resetToCamera();
// Drawing controls
this.setupDrawingEvents();
this.setupToolButtons();
// Settings changes
this.colorPicker.oninput = (e) => this.ctx.strokeStyle = e.target.value;
this.brushSize.oninput = (e) => {
this.ctx.lineWidth = e.target.value;
this.brushSizeValue.textContent = `${e.target.value}px`;
};
// History controls
this.undoButton.onclick = () => this.undo();
this.resetButton.onclick = () => this.resetDrawing();
// Save functionality
this.saveButton.onclick = () => this.saveAndUpload();
// Text tool modal controls
this.confirmTextBtn.onclick = () => {
const text = this.textInput.value.trim();
if (text) {
this.addDraggableText(text);
this.textInput.value = '';
}
};
}
setupToolButtons() {
const toolButtons = document.querySelectorAll('.tool-btn');
toolButtons.forEach(button => {
button.onclick = () => {
toolButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
this.currentTool = button.dataset.tool;
};
});
}
// Implement drawing events from previous code here
setupDrawingEvents() {
// Mouse events
this.drawingCanvas.addEventListener('mousedown', (e) => this.startDrawing(e));
this.drawingCanvas.addEventListener('mousemove', (e) => this.draw(e));
this.drawingCanvas.addEventListener('mouseup', () => this.stopDrawing());
this.drawingCanvas.addEventListener('mouseout', () => this.stopDrawing());
// Touch events
this.drawingCanvas.addEventListener('touchstart', (e) => {
if (this.shouldPreventDefault(e)) {
e.preventDefault();
}
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
this.startDrawing(mouseEvent);
}, {
passive: false
});
this.drawingCanvas.addEventListener('touchmove', (e) => {
if (this.shouldPreventDefault(e)) {
e.preventDefault();
}
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
this.draw(mouseEvent);
}, {
passive: false
});
this.drawingCanvas.addEventListener('touchend', () => {
this.stopDrawing();
}, {
passive: true
});
}
// Implement drawing methods from previous code here
startDrawing(e) {
this.isDrawing = true;
const rect = this.drawingCanvas.getBoundingClientRect();
const scaleX = this.drawingCanvas.width / rect.width;
const scaleY = this.drawingCanvas.height / rect.height;
this.startX = (e.clientX - rect.left) * scaleX;
this.startY = (e.clientY - rect.top) * scaleY;
this.lastX = this.startX;
this.lastY = this.startY;
if (this.currentTool === 'brush') {
this.ctx.beginPath();
this.ctx.moveTo(this.startX, this.startY);
}
}
addDraggableText(text) {
const textElement = document.createElement('div');
textElement.className = 'draggable-text';
textElement.style.cssText = `
position: absolute;
cursor: move;
user-select: none;
color: ${this.colorPicker.value};
font-size: ${this.brushSize.value * 2}px;
padding: 5px;
border-radius: 3px;
display: flex;
align-items: center;
gap: 8px;
`;
// Create text span
const textSpan = document.createElement('span');
textSpan.textContent = text;
textElement.appendChild(textSpan);
// Create delete button
const deleteBtn = document.createElement('button');
deleteBtn.innerHTML = '×';
deleteBtn.style.cssText = `
border: none;
background: none;
color: #ff4444;
font-size: 20px;
cursor: pointer;
padding: 0;
line-height: 1;
opacity: 0;
transition: opacity 0.2s;
`;
textElement.appendChild(deleteBtn);
// Show/hide delete button on hover
textElement.addEventListener('mouseenter', () => {
deleteBtn.style.opacity = '1';
});
textElement.addEventListener('mouseleave', () => {
deleteBtn.style.opacity = '0';
});
// Delete functionality
deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
textElement.remove();
this.saveDrawingState();
});
// Center the text initially
textElement.style.left = '50%';
textElement.style.top = '50%';
textElement.style.transform = 'translate(-50%, -50%)';
// Add drag functionality
this.setupDraggableText(textElement);
this.textOverlay.appendChild(textElement);
this.saveDrawingState();
}
setupDraggableText(element) {
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 0;
let yOffset = 0;
const dragStart = (e) => {
if (this.currentTool === 'text') {
const event = e.type === 'mousedown' ? e : e.touches[0];
initialX = event.clientX - xOffset;
initialY = event.clientY - yOffset;
if (e.target === element || e.target.parentNode === element) {
isDragging = true;
}
}
};
const drag = (e) => {
if (isDragging) {
e.preventDefault();
const event = e.type === 'mousemove' ? e : e.touches[0];
currentX = event.clientX - initialX;
currentY = event.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, element);
}
};
const dragEnd = () => {
if (isDragging) {
isDragging = false;
this.saveDrawingState();
}
};
const setTranslate = (xPos, yPos, el) => {
el.style.transform = `translate(${xPos}px, ${yPos}px)`;
};
// Mouse events
element.addEventListener('mousedown', dragStart);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', dragEnd);
// Touch events
element.addEventListener('touchstart', dragStart);
document.addEventListener('touchmove', drag);
document.addEventListener('touchend', dragEnd);
}
// Add this CSS to your stylesheet
draw(e) {
if (!this.isDrawing) return;
const rect = this.drawingCanvas.getBoundingClientRect();
const scaleX = this.drawingCanvas.width / rect.width;
const scaleY = this.drawingCanvas.height / rect.height;
const x = (e.clientX - rect.left) * scaleX;
const y = (e.clientY - rect.top) * scaleY;
switch (this.currentTool) {
case 'arrow':
// Restore the previous state before drawing new arrow
if (this.historyIndex >= 0) {
this.redrawHistory();
} else {
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
}
this.drawArrow(this.startX, this.startY, x, y);
break;
case 'brush':
this.ctx.lineTo(x, y);
this.ctx.stroke();
break;
case 'rectangle':
// Restore the previous state before drawing new rectangle
if (this.historyIndex >= 0) {
this.redrawHistory();
} else {
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
}
this.ctx.beginPath();
this.ctx.strokeRect(this.startX, this.startY, x - this.startX, y - this.startY);
break;
case 'circle':
// Restore the previous state before drawing new circle
if (this.historyIndex >= 0) {
this.redrawHistory();
} else {
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
}
const radius = Math.sqrt(Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2));
this.ctx.beginPath();
this.ctx.arc(this.startX, this.startY, radius, 0, Math.PI * 2);
this.ctx.stroke();
break;
}
this.lastX = x;
this.lastY = y;
}
drawArrow(fromX, fromY, toX, toY) {
const headLength = 20;
const headAngle = Math.PI / 6;
// Calculate angle
const angle = Math.atan2(toY - fromY, toX - fromX);
// Draw main line
this.ctx.beginPath();
this.ctx.moveTo(fromX, fromY);
this.ctx.lineTo(toX, toY);
this.ctx.stroke();
// Draw arrowhead
this.ctx.beginPath();
this.ctx.moveTo(toX, toY);
this.ctx.lineTo(
toX - headLength * Math.cos(angle - headAngle),
toY - headLength * Math.sin(angle - headAngle)
);
this.ctx.moveTo(toX, toY);
this.ctx.lineTo(
toX - headLength * Math.cos(angle + headAngle),
toY - headLength * Math.sin(angle + headAngle)
);
this.ctx.stroke();
}
startDrawing(e) {
this.isDrawing = true;
const rect = this.drawingCanvas.getBoundingClientRect();
const scaleX = this.drawingCanvas.width / rect.width;
const scaleY = this.drawingCanvas.height / rect.height;
this.startX = (e.clientX - rect.left) * scaleX;
this.startY = (e.clientY - rect.top) * scaleY;
this.lastX = this.startX;
this.lastY = this.startY;
if (this.currentTool === 'brush') {
this.ctx.beginPath();
this.ctx.moveTo(this.startX, this.startY);
}
}
stopDrawing() {
if (this.isDrawing) {
this.isDrawing = false;
if (this.currentTool === 'brush') {
this.ctx.closePath();
}
this.saveDrawingState();
}
}
redrawHistory() {
if (this.historyIndex >= 0 && this.drawingHistory[this.historyIndex]) {
const img = new Image();
img.src = this.drawingHistory[this.historyIndex];
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
this.ctx.drawImage(img, 0, 0);
} else {
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
}
}
closeEditor() {
if (this.stream) {
this.stream.getTracks().forEach(track => track.stop());
}
// Clear canvases and text overlay
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
const mainCtx = this.canvas.getContext('2d');
mainCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.textOverlay.innerHTML = '';
// Reset state
this.drawingHistory = [];
this.historyIndex = -1;
this.currentInputField = null;
// Close modals
this.cameraModal.hide();
this.textInputModal.hide();
}
// Override this method in your implementation
saveAndUpload() {
}
closeCamera() {
// Stop the video stream
if (this.stream) {
this.stream.getTracks().forEach(track => track.stop());
}
// Hide video and canvas elements
this.video.style.display = 'none';
this.canvas.style.display = 'none';
this.drawingCanvas.style.display = 'none';
// Reset any additional state if needed
this.stream = null;
}
}
</script>
@endpush

View File

@@ -0,0 +1,185 @@
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
const ruteLainnyaDiv = document.getElementById("ruteLainnya");
const lantaiLainnyaDiv = document.getElementById("lantaiLainnya");
// Create new div for additional items
function createNewDiv(container, inputName) {
const newDiv = document.createElement("div");
newDiv.className = "flex items-baseline flex-wrap lg:flex-nowrap gap-2.5 mb-5";
const index = container.querySelectorAll(".flex.items-baseline")
.length; // Get index for dynamic IDs
newDiv.innerHTML = `
<div class="flex items-baseline w-full flex-wrap lg:flex-nowrap gap-2.5 mb-5" id="photoContainer">
<label class="flex flex-col form-label max-w-56">
Masukkan nama ${inputName}
</label>
<div class="flex flex-wrap items-baseline w-full gap-4">
<img id="foto_${inputName}-preview-${index}"
src="{{ isset($formFoto['gerbang']) ? asset('storage/' . $formFoto['gerbang']) : '' }}"
alt="Foto Gerbong" class="mt-2 max-w-full h-auto"
style="{{ isset($formFoto['gerbang']) ? '' : 'display: none;' }} width: 30rem;">
<div class="flex flex-col lg:flex-row gap-2 w-full">
<div class="flex flex-wrap items-baseline px-2">
<input class="input" type="text" name="name_${inputName}[]">
</div>
<div class="input-group w-full flex flex-col gap-2">
<div class="input-group w-full flex gap-2">
<input id="inputLainnya-${index}" type="file" name="foto_${inputName}[]"
class="file-input file-input-bordered w-full" accept="image/*"
capture="camera" onchange="previewImage(this, 'foto_${inputName}-preview-${index}')">
<button type="button" id="btnCamera-${index}" class="btn btn-light"
data-modal-toggle="#cameraModal">
<i class="ki-outline ki-abstract-33"></i> Camera
</button>
</div>
</div>
<button type="button" class="btn btn-danger btn-sm delete-btn">
<i class="ki-filled ki-trash"></i>
</button>
</div>
</div>
</div>
`;
container.appendChild(newDiv);
addDeleteListeners(container); // Re-add listeners for delete buttons in the new div
}
// Event listener for adding more items
function setupInputHandlers(containerId, buttonId, labelText, inputDataClass, buttonDeleteClass) {
const addButton = document.getElementById(buttonId);
const inputContainer = document.getElementById(containerId);
if (!addButton || !inputContainer) {
console.error(`Element with ID ${containerId} or ${buttonId} not found.`);
return;
}
function updateLabels() {
const labels = inputContainer.querySelectorAll('.form-label span');
labels.forEach((label, index) => {
label.textContent = `${labelText} ${index + 1}`;
});
}
function handleDeleteButtons() {
const deleteBtns = inputContainer.querySelectorAll(`.${buttonDeleteClass}`);
deleteBtns.forEach(btn => {
btn.style.display = inputContainer.children.length > 1 ? 'block' : 'none';
});
}
function createNewInput() {
const newDiv = inputContainer.children[0].cloneNode(true);
// Reset semua input dalam elemen baru
const inputFile = newDiv.querySelector(`.${inputDataClass}`);
const previewContainer = newDiv.querySelector('.preview-container');
const imgPreview = previewContainer.querySelector('img');
if (inputFile) {
// Generate unique ID untuk input dan preview
const uniqueId = `${containerId}-${Date.now()}`;
inputFile.id = `input-${uniqueId}`;
imgPreview.id = `preview-${uniqueId}`;
// Reset input file
inputFile.value = '';
// Tambahkan event listener untuk preview
inputFile.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
imgPreview.src = e.target.result;
imgPreview.style.display = 'block';
};
reader.readAsDataURL(file);
}
});
}
// Reset preview
imgPreview.src = '';
imgPreview.style.display = 'none';
const deleteBtn = newDiv.querySelector(`.${buttonDeleteClass}`);
if (deleteBtn) {
deleteBtn.addEventListener('click', function() {
inputContainer.removeChild(newDiv);
handleDeleteButtons();
updateLabels();
});
}
newDiv.style.marginTop = '10px';
inputContainer.appendChild(newDiv);
updateLabels();
handleDeleteButtons();
}
// Event listener untuk tombol tambah
addButton.addEventListener('click', createNewInput);
// Inisialisasi preview untuk input yang sudah ada
const existingInputs = inputContainer.querySelectorAll(`.${inputDataClass}`);
existingInputs.forEach((input, index) => {
const previewContainer = input.closest('.flex').querySelector('.preview-container');
const imgPreview = previewContainer.querySelector('img');
input.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
imgPreview.src = e.target.result;
imgPreview.style.display = 'block';
};
reader.readAsDataURL(file);
}
});
// Tambahkan event listener untuk tombol hapus
document.querySelectorAll('.delete-btn').forEach(deleteBtn => {
deleteBtn.addEventListener('click', function() {
// Temukan container terdekat yang akan dihapus
const containerToRemove = this.closest('.flex');
const inputContainer = this.closest('#inputContainerRute');
if (inputContainer.querySelectorAll('.flex').length > 1) {
containerToRemove.remove();
// Update nomor label setelah penghapusan
updateLabels(inputContainer);
} else {
// Jika hanya satu input, reset input tersebut
resetInput(containerToRemove);
}
});
});
});
updateLabels();
handleDeleteButtons();
}
// setupInputHandlers('inputContainerRute', 'btnRute', 'Foto Rute Menuju Lokasi', 'file-input',
// 'delete-btn');
// setupInputHandlers('inputContainerLantai', 'btnLantai', 'Foto Lantai', 'file-input',
// 'delete-btn');
// setupInputHandlers('inputContainerLingkungan', 'btnLingkungan', 'Lingkungan', 'file-input',
// 'delete-btn');
});
</script>

View File

@@ -0,0 +1,538 @@
<script>
function showLoadingSwal(message) {
Swal.fire({
title: message,
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
}
});
}
function hideLoadingSwal() {
Swal.close();
}
function previewImage(input, imageId) {
const preview = document.getElementById(imageId);
if (input.files && input.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
preview.src = e.target.result;
preview.classList.remove('hidden');
}
reader.readAsDataURL(input.files[0]);
}
}
function formatNumber(input) {
const cursorPosition = input.selectionStart;
let value = input.value.replace(/[^0-9,]/g, '');
if ((value.match(/,/g) || []).length > 1) {
value = value.replace(/,/g, (match, offset) => (offset === value.indexOf(',') ? ',' : ''));
}
if (value.includes(',')) {
const [beforeComma, afterComma] = value.split(',');
value = `${beforeComma},${afterComma.substring(0, 2)}`;
}
input.value = value ? value + ' m²' : 'm²';
input.setSelectionRange(cursorPosition, cursorPosition);
}
function formatCurrency(value, isDiskon = false) {
// Ensure the value is a valid number
const numericValue = parseFloat(value);
if (isNaN(numericValue)) {
return 0;
}
// Format the number with commas for thousands separators
const formattedValue = numericValue.toLocaleString('id-ID', {
style: 'currency',
currency: 'IDR', // Indonesian Rupiah
minimumFractionDigits: isDiskon ? 2 : 0, // Include decimals for discounts
maximumFractionDigits: isDiskon ? 2 : 0,
});
return formattedValue;
}
function previewImage(input, previewId) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('#' + previewId).attr('src', e.target.result).show();
}
reader.readAsDataURL(input.files[0]);
} else {
$('#' + previewId).hide();
}
}
function addClonableItem(containerId, itemClass) {
const container = document.getElementById(containerId);
if (!container) {
console.error(`Container with ID "${containerId}" not found.`);
return;
}
const template = container.querySelector(`.${itemClass}`);
if (!template) {
console.error(`Template with class "${itemClass}" not found in container "${containerId}".`);
return;
}
// Clone the template element
const newElement = template.cloneNode(true);
// Reset all input fields comprehensively
const inputs = newElement.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
// Reset based on input type
switch (input.type) {
case 'text':
case 'number':
case 'email':
case 'tel':
case 'password':
case 'search':
case 'url':
case 'textarea':
input.value = '';
break;
case 'checkbox':
case 'radio':
input.checked = false;
break;
case 'select-one':
case 'select-multiple':
input.selectedIndex = 0;
break;
case 'file':
input.value = '';
break;
}
// Remove any error classes or validation states
input.classList.remove('is-invalid', 'error');
// Reset any custom attributes if needed
input.removeAttribute('data-previous-value');
});
// Reset select elements to their default state
const selects = newElement.querySelectorAll('select');
selects.forEach(select => {
select.selectedIndex = 0;
});
// Make the delete button visible and bind the click event
const deleteButton = newElement.querySelector('.remove-btn');
if (deleteButton) {
deleteButton.style.display = 'inline-block';
deleteButton.addEventListener('click', () => {
newElement.remove();
});
}
// Reset any custom data attributes
newElement.querySelectorAll('[data-clone-reset]').forEach(element => {
const resetValue = element.getAttribute('data-clone-reset');
if (resetValue) {
if (element.tagName === 'INPUT' || element.tagName === 'SELECT' || element.tagName ===
'TEXTAREA') {
element.value = resetValue;
} else {
element.textContent = resetValue;
}
}
});
// Optional: Regenerate unique IDs if needed
const elementsWithId = newElement.querySelectorAll('[id]');
elementsWithId.forEach(element => {
const originalId = element.id;
const newId = `${originalId}_${Date.now()}`;
element.id = newId;
// Update any labels or references
const labels = newElement.querySelectorAll(`label[for="${originalId}"]`);
labels.forEach(label => {
label.setAttribute('for', newId);
});
});
// Append the cloned element to the container
container.appendChild(newElement);
// Optional: Trigger any custom events or reinitialize plugins
const event = new Event('cloneAdded', {
bubbles: true
});
newElement.dispatchEvent(event);
}
async function loadSavedLocationData() {
const provinceCode = '{{ $cekAlamat['province_code'] ?? '' }}';
const cityCode = '{{ $cekAlamat['city_code'] ?? '' }}';
const districtCode = '{{ $cekAlamat['district_code'] ?? '' }}';
const villageCode = '{{ $cekAlamat['village_code'] ?? '' }}';
// Set province
const provinceSelect = document.getElementById('province_code');
if (provinceCode && provinceSelect) {
provinceSelect.value = provinceCode;
await getCity(provinceCode);
}
// Set city
const citySelect = document.getElementById('city_code');
if (cityCode && citySelect) {
citySelect.value = cityCode;
await getDistrict(cityCode);
}
// Set district
const districtSelect = document.getElementById('district_code');
if (districtCode && districtSelect) {
districtSelect.value = districtCode;
await getVillage(districtCode);
}
// Set village
const villageSelect = document.getElementById('village_code');
if (villageCode && villageSelect) {
villageSelect.value = villageCode;
}
}
// Modifikasi fungsi existing
async function getCity(provinceId) {
try {
const response = await fetch(`/locations/cities/province/${provinceId}`);
const data = await response.json();
const cityDropdown = document.getElementById('city_code');
if (cityDropdown) {
cityDropdown.innerHTML = '<option value="">Pilih Kota/Kabupaten</option>';
data.forEach(city => {
const option = document.createElement('option');
option.value = city.code;
option.textContent = city.name;
@if (isset($debitur->city_code))
if (city.code === '{{ $debitur->city_code }}') {
option.selected = true;
}
@endif
cityDropdown.appendChild(option);
});
// Reset dropdown kecamatan dan desa
document.getElementById('district_code').innerHTML = '<option value="">Pilih Kecamatan</option>';
document.getElementById('village_code').innerHTML = '<option value="">Pilih Kelurahan</option>';
}
} catch (error) {
console.error('Error fetching cities:', error);
}
}
// Lakukan hal serupa untuk getDistrict dan getVillage
async function getDistrict(cityId) {
try {
const response = await fetch(`/locations/districts/city/${cityId}`);
const data = await response.json();
const districtDropdown = document.getElementById('district_code');
if (districtDropdown) {
districtDropdown.innerHTML = '<option value="">Pilih Kecamatan</option>';
data.forEach(district => {
const option = document.createElement('option');
option.value = district.code;
option.textContent = district.name;
// Cek apakah ini adalah kecamatan yang sebelumnya dipilih
@if (isset($debitur->district_code))
if (district.code === '{{ $debitur->district_code }}') {
option.selected = true;
}
@endif
districtDropdown.appendChild(option);
});
// Reset dropdown desa
document.getElementById('village_code').innerHTML = '<option value="">Pilih Kelurahan</option>';
}
} catch (error) {
console.error('Error fetching districts:', error);
}
}
async function getVillage(districtId) {
try {
const response = await fetch(`/locations/villages/district/${districtId}`);
const data = await response.json();
const villageDropdown = document.getElementById('village_code');
if (villageDropdown) {
villageDropdown.innerHTML = '<option value="">Pilih Desa/Kelurahan</option>';
data.forEach(village => {
const option = document.createElement('option');
option.value = village.code;
option.textContent = village.name;
// Cek apakah ini adalah desa yang sebelumnya dipilih
@if (isset($debitur->village_code))
if (village.code === '{{ $debitur->village_code }}') {
option.selected = true;
}
@endif
villageDropdown.appendChild(option);
});
}
} catch (error) {
console.error('Error fetching villages:', error);
}
}
function checkLaporan(permohonanId, documentId, inspeksiId, statusLpj) {
// showLoadingSwal('Tunggu...');
fetch(
`{{ url('/penilai/check-laporan') }}?permohonanId=${permohonanId}&documentId=${documentId}&inspeksiId=${inspeksiId}`
)
.then(response => response.json())
.then(data => {
if (data.status) {
window.location.href =
`{{ route('penilai.print-out') }}?permohonanId=${permohonanId}&documentId=${documentId}&inspeksiId=${inspeksiId}&statusLpj=${statusLpj}&type=${data.status}`;
} else {
// Jika laporan belum ada, tampilkan pesan peringatan
Swal.fire({
title: 'Laporan Belum Ada',
text: data.message,
icon: 'warning',
confirmButtonText: 'OK',
confirmButtonColor: '#3085d6',
});
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire({
title: 'Terjadi Kesalahan',
text: 'Tidak dapat memproses permintaan. Silakan coba lagi nanti.',
icon: 'error',
confirmButtonText: 'OK',
confirmButtonColor: '#d33',
});
});
}
function updateAnalisa(params) {
const inputMap = {
jenis_asset: 'jenis_asset_tidak_sesuai',
analisa_tanah: 'analisa_tanah_tidak_sesuai',
analisa_unit: 'analisa_luas_unit_tidak_sesuai',
analisa_bangunan: 'analisa_bangunan_tidak_sesuai',
};
// Pastikan elemen ID ada di inputMap
if (!inputMap[params]) {
console.error('Parameter tidak valid:', params);
return;
}
// Ambil nilai berdasarkan parameter
const inputValue = document.getElementById(inputMap[params]).value;
const data = {
[params === 'jenis_asset' ? 'jenis_asset' : params.replace('analisa_', 'luas_')]: inputValue,
types: params,
...(document.getElementById('jenis_legalistas_jaminan_bangunan_id') && {
jenis_legalistas_jaminan_bangunan_id: document.getElementById(
'jenis_legalistas_jaminan_bangunan_id').value
}),
...(document.getElementById('jenis_legalistas_jaminan_tanah_id') && {
jenis_legalistas_jaminan_tanah_id: document.getElementById('jenis_legalistas_jaminan_tanah_id')
.value
}),
...(document.getElementById('jenis_legalistas_jaminan_unit_id') && {
jenis_legalistas_jaminan_unit_id: document.getElementById('jenis_legalistas_jaminan_unit_id')
.value
})
};
console.log(data);
$.ajax({
url: '{{ route('surveyor.update_analisa', ['id' => $permohonan->id]) }}',
type: 'POST',
data: data,
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
if (response.success) {
// window.location.href =
// '{{ route('surveyor.show', ['id' => $permohonan->id]) }}';
toastrSuccessBuild(response.message);
}
},
error: function(xhr, status, error) {
console.error('Terjadi error:', error);
console.log('Status:', status);
console.log('Response:', xhr.responseText);
if (xhr.responseJSON.message) {
toastrErrorBuild(xhr.responseJSON.message);
} else {
toastrErrorBuild('Terjadi kesalahan');
}
}
});
}
function updateAlamatFields(status) {
// Ambil elemen formulir
const addressForm = document.getElementById('alamat_form');
const inputs = addressForm.querySelectorAll('input, select');
const addressInput = document.getElementById('address');
if (status === 'sesuai') {
addressInput.value = "{{ $dokumen->address ?? '' }}";
inputs.forEach(element => {
if (element.tagName === 'INPUT') {
element.setAttribute('readonly', true);
} else if (element.tagName === 'SELECT') {
element.setAttribute('disabled', true);
element.classList.add('disabled-input')
}
});
addressForm.style.display = 'grid';
addressForm.disabled = true;
addressForm.classList.add('disabled-input')
} else if (status === 'tidak sesuai') {
addressForm.style.display = 'grid';
addressForm.removeAttribute('disabled');
addressForm.classList.remove('disabled-input')
const formInspeksi = @json($forminspeksi ?? '');
const addressInput = document.getElementById('address');
if (formInspeksi && formInspeksi.asset && formInspeksi.asset.alamat) {
if (formInspeksi.asset.alamat['tidak sesuai'] && formInspeksi.asset.alamat['tidak sesuai'].address) {
addressInput.value = formInspeksi.asset.alamat['tidak sesuai'].address;
} else if (formInspeksi.asset.alamat['sesuai'] && formInspeksi.asset.alamat['sesuai'].address) {
addressInput.value = formInspeksi.asset.alamat['sesuai'].address;
} else {
addressInput.value = "";
}
}
inputs.forEach(element => {
if (element.tagName === 'INPUT') {
element.removeAttribute('readonly');
} else if (element.tagName === 'SELECT') {
element.removeAttribute('disabled');
element.classList.remove('disabled-input')
}
});
}
}
function toggleFieldVisibility(fieldName, inputId, visibleValues = []) {
const selectedValue = $(`[name="${fieldName}"]:checked`).val();
const inputField = $(`#${inputId}`);
if (visibleValues.includes(selectedValue)) {
inputField.show();
} else {
inputField.hide().val('');
}
}
function toggleCheckboxVisibility(fieldName, inputId, visibleValues = []) {
const selectedValues = $(`[name="${fieldName}[]"]:checked`)
.map(function() {
return $(this).val().toLowerCase(); // Konversi nilai ke huruf kecil
})
.get();
const inputField = $(`#${inputId}`);
// Cek apakah salah satu nilai yang dipilih cocok dengan visibleValues
const shouldShow = visibleValues.some(value => selectedValues.includes(value.toLowerCase()));
if (shouldShow) {
inputField.show();
} else {
inputField.hide().val('');
}
}
function toggleMultipleFields(fieldName, mappings) {
// Ambil semua nilai checkbox yang dicentang
const selectedValues = $(`[name="${fieldName}[]"]:checked`)
.map(function() {
return $(this).val().toLowerCase(); // Konversi nilai ke huruf kecil
})
.get();
// Iterasi melalui setiap mapping
for (const [key, inputId] of Object.entries(mappings)) {
const inputField = $(`#${inputId}`);
// Tampilkan input jika nilai yang relevan dipilih
if (selectedValues.includes(key.toLowerCase())) {
inputField.show();
} else {
inputField.hide().val(''); // Sembunyikan dan reset nilai
}
}
}
function toggleAlamatVisibility(idSesuai, idTidakSesuai, selectedValue) {
// Ambil elemen berdasarkan ID
const alamatSesuai = document.getElementById(idSesuai);
const alamatTidakSesuai = document.getElementById(idTidakSesuai);
// Periksa nilai yang dipilih dan tampilkan elemen yang sesuai
if (selectedValue === 'sesuai') {
alamatSesuai.style.display = 'grid'; // Tampilkan "Alamat Sesuai"
alamatTidakSesuai.style.display = 'none'; // Sembunyikan "Alamat Tidak Sesuai"
} else if (selectedValue === 'tidak sesuai') {
alamatSesuai.style.display = 'none'; // Sembunyikan "Alamat Sesuai"
alamatTidakSesuai.style.display = 'grid'; // Tampilkan "Alamat Tidak Sesuai"
}
}
function handleCurrencyInput(input) {
const value = input.value.replace(/[^\d]/g, '');
input.value = formatCurrency(value);
}
function cleanCurrencyValue(value) {
return value.replace(/[^\d]/g, '');
}
</script>