Initial Commit
This commit is contained in:
760
resources/views/surveyor/js/camera-editor.blade.php
Normal file
760
resources/views/surveyor/js/camera-editor.blade.php
Normal 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
|
||||
185
resources/views/surveyor/js/fotojs.blade.php
Normal file
185
resources/views/surveyor/js/fotojs.blade.php
Normal 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>
|
||||
538
resources/views/surveyor/js/utils.blade.php
Normal file
538
resources/views/surveyor/js/utils.blade.php
Normal 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>
|
||||
Reference in New Issue
Block a user