521 lines
21 KiB
PHP
521 lines
21 KiB
PHP
<script type="text/javascript">
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
const ruteLainnyaDiv = document.getElementById("ruteLainnya");
|
|
const lantaiLainnyaDiv = document.getElementById("lantaiLainnya");
|
|
|
|
// Function to add delete event listeners to existing buttons
|
|
function addDeleteListeners(container) {
|
|
container.querySelectorAll(".delete-btn").forEach(button => {
|
|
button.addEventListener("click", function() {
|
|
this.closest(
|
|
".flex.items-baseline.flex-wrap.lg\\:flex-nowrap.gap-2\\.5.mb-5")
|
|
.remove();
|
|
});
|
|
});
|
|
}
|
|
|
|
// Add delete listeners to existing buttons
|
|
addDeleteListeners(ruteLainnyaDiv);
|
|
|
|
// 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
|
|
document.getElementById("btnAddMore").addEventListener("click", function() {
|
|
createNewDiv(ruteLainnyaDiv, "rute_lainnya");
|
|
});
|
|
|
|
document.getElementById("btnAddMoreObject").addEventListener("click", function() {
|
|
createNewDiv(lantaiLainnyaDiv, "lantai_lainnya");
|
|
});
|
|
|
|
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');
|
|
|
|
|
|
});
|
|
|
|
class DynamicFileUploader {
|
|
constructor(options) {
|
|
const defaultOptions = {
|
|
containerId: 'lantaiContainer',
|
|
addButtonId: null,
|
|
fileInputName: 'foto_lantai_unit',
|
|
maxFiles: 4,
|
|
allowMultiple: true,
|
|
name: null,
|
|
accept: 'image/*',
|
|
existingFiles: []
|
|
};
|
|
|
|
this.options = {
|
|
...defaultOptions,
|
|
...options
|
|
};
|
|
this.container = document.getElementById(this.options.containerId);
|
|
|
|
if (this.options.addButtonId) {
|
|
this.addButton = document.getElementById(this.options.addButtonId);
|
|
|
|
this.addButton.addEventListener('click', () => {
|
|
this.addNewItem();
|
|
});
|
|
}
|
|
|
|
|
|
// Inisialisasi global handlers
|
|
this.initGlobalHandlers();
|
|
this.init();
|
|
}
|
|
|
|
initGlobalHandlers() {
|
|
// Simpan instance ke window untuk akses global
|
|
// window.dynamicFileUploader = this;
|
|
|
|
// Global handler untuk drag and drop
|
|
window.handleDrop = (event) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
const dropzone = event.currentTarget;
|
|
const fileInput = dropzone.querySelector('.file-input');
|
|
this.handleDrop(event, fileInput);
|
|
};
|
|
|
|
window.allowDrop = (event) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
};
|
|
|
|
window.handleFileInput = (fileInput) => {
|
|
this.handleFileInput(fileInput);
|
|
};
|
|
}
|
|
|
|
init() {
|
|
// Proses file yang sudah ada
|
|
if (this.options.existingFiles && this.options.existingFiles.length > 0) {
|
|
this.processExistingFiles();
|
|
}
|
|
|
|
// Inisialisasi item pertama jika belum ada file
|
|
if (this.container.children.length === 0) {
|
|
this.addNewItem();
|
|
}
|
|
|
|
// Inisialisasi event listener untuk item yang sudah ada
|
|
this.container.querySelectorAll(".dynamic-item").forEach(item => {
|
|
this.attachEventListeners(item);
|
|
});
|
|
|
|
this.updateRemoveButtonVisibility();
|
|
}
|
|
|
|
addNewItem() {
|
|
const currentItemCount = this.container.children.length + 1;
|
|
const newItem = document.createElement("div");
|
|
newItem.classList.add("dynamic-item", "w-full", "mb-4");
|
|
newItem.innerHTML = this.generateItemHTML(currentItemCount);
|
|
this.container.appendChild(newItem);
|
|
|
|
if (currentItemCount > 1) {
|
|
this.attachEventListeners(newItem);
|
|
}
|
|
this.updateRemoveButtonVisibility();
|
|
return newItem;
|
|
}
|
|
|
|
generateItemHTML(itemCount) {
|
|
return `
|
|
<div class="flex w-full items-center justify-between">
|
|
${this.options.name ? `<label class="form-label">${this.options.name} ${itemCount}</label>` : ''}
|
|
<button type="button" class="btn btn-danger btn-sm btnRemoveLantai"
|
|
style="${itemCount === 1 ? 'display: none;' : ''}">
|
|
Hapus
|
|
</button>
|
|
</div>
|
|
<div class="dropzone w-full border-2 border-dashed border-gray-400 rounded-lg p-4 mt-2"
|
|
ondrop="handleDrop(event)" ondragover="allowDrop(event)">
|
|
<div class="preview-container grid grid-cols-2 md:grid-cols-4 gap-4"></div>
|
|
<input type="file"
|
|
name="${this.options.fileInputName}[${itemCount}][]"
|
|
class="file-input hidden"
|
|
multiple
|
|
accept="${this.options.accept}"
|
|
onchange="handleFileInput(this)"
|
|
data-lantai="${itemCount}">
|
|
<button type="button" class="btn btn-light btn-sm btnUploadFiles mt-2">
|
|
<i class="ki-outline ki-upload"></i> Unggah Gambar
|
|
</button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
attachEventListeners(item) {
|
|
const uploadButton = item.querySelector('.btnUploadFiles');
|
|
const fileInput = item.querySelector('.file-input');
|
|
const previewContainer = item.querySelector('.preview-container');
|
|
|
|
if (!uploadButton.dataset.listenerAttached) {
|
|
uploadButton.addEventListener('click', () => {
|
|
fileInput.click();
|
|
});
|
|
uploadButton.dataset.listenerAttached = true;
|
|
}
|
|
|
|
const removeItemButton = item.querySelector('.btnRemoveLantai');
|
|
if (removeItemButton && !removeItemButton.dataset.listenerAttached) {
|
|
removeItemButton.addEventListener('click', () => {
|
|
this.removeItem(item);
|
|
});
|
|
removeItemButton.dataset.listenerAttached = true;
|
|
}
|
|
}
|
|
|
|
|
|
handleDrop(event, fileInput) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
const files = event.dataTransfer.files;
|
|
const previewContainer = fileInput.closest('.dropzone').querySelector('.preview-container');
|
|
this.processFiles(files, fileInput, previewContainer);
|
|
}
|
|
|
|
handleFileInput(fileInput) {
|
|
if (!fileInput || !fileInput.files) return;
|
|
|
|
const files = fileInput.files;
|
|
const dropzone = fileInput.closest('.dropzone');
|
|
const previewContainer = dropzone.querySelector('.preview-container');
|
|
|
|
// Proses file
|
|
this.processFiles(files, fileInput, previewContainer);
|
|
}
|
|
|
|
processFiles(files, fileInput, previewContainer) {
|
|
const existingPreviews = previewContainer.querySelectorAll('.preview-item img');
|
|
const existingFiles = Array.from(existingPreviews).map(img => img.src);
|
|
|
|
const maxFiles = this.options.maxFiles;
|
|
const remainingSlots = maxFiles - existingPreviews.length;
|
|
|
|
const filesToAdd = Array.from(files).filter(file => {
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
return !existingFiles.includes(e.target.result);
|
|
};
|
|
reader.readAsDataURL(file);
|
|
return true;
|
|
}).slice(0, remainingSlots);
|
|
|
|
if (filesToAdd.length === 0) {
|
|
alert(`Maksimal ${maxFiles} foto per lantai`);
|
|
return;
|
|
}
|
|
|
|
filesToAdd.forEach(file => {
|
|
const isValidType = this.options.accept === '*' || file.type.match(this.options.accept
|
|
.replace('*', '.*'));
|
|
|
|
if (isValidType) {
|
|
this.createFilePreview(file, previewContainer, fileInput);
|
|
} else {
|
|
alert(`File ${file.name} tidak valid. Hanya file gambar yang diperbolehkan.`);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
createFilePreview(file, previewContainer, fileInput) {
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
const imgWrapper = document.createElement('div');
|
|
imgWrapper.classList.add('preview-item', 'relative');
|
|
|
|
const img = document.createElement('img');
|
|
img.src = e.target.result;
|
|
img.classList.add('w-full', 'h-40', 'object-cover', 'rounded-lg');
|
|
|
|
const removeBtn = document.createElement('button');
|
|
removeBtn.type = 'button';
|
|
removeBtn.classList.add(
|
|
'absolute', 'top-2', 'right-2',
|
|
'btn', 'btn-sm', 'btn-danger', 'btn-icon',
|
|
'btnRemoveFoto'
|
|
);
|
|
removeBtn.innerHTML = '<i class="ki-solid ki-cross"></i>';
|
|
|
|
removeBtn.addEventListener('click', () => {
|
|
imgWrapper.remove();
|
|
this.updatePreviewContainerVisibility(previewContainer);
|
|
this.resetFileInput(fileInput);
|
|
});
|
|
|
|
imgWrapper.appendChild(img);
|
|
imgWrapper.appendChild(removeBtn);
|
|
previewContainer.appendChild(imgWrapper);
|
|
|
|
this.updatePreviewContainerVisibility(previewContainer);
|
|
};
|
|
|
|
reader.readAsDataURL(file);
|
|
}
|
|
|
|
updatePreviewContainerVisibility(previewContainer) {
|
|
const previewItems = previewContainer.querySelectorAll('.preview-item');
|
|
previewContainer.style.display = previewItems.length > 0 ? 'grid' : 'none';
|
|
}
|
|
|
|
resetFileInput(fileInput) {
|
|
fileInput.value = '';
|
|
}
|
|
|
|
removeItem(item) {
|
|
this.container.removeChild(item);
|
|
this.updateRemoveButtonVisibility();
|
|
}
|
|
|
|
updateRemoveButtonVisibility() {
|
|
const removeButtons = this.container.querySelectorAll('.btnRemoveLantai');
|
|
removeButtons.forEach((btn, index) => {
|
|
btn.style.display = index === 0 ? 'none' : 'block';
|
|
});
|
|
}
|
|
|
|
processExistingFiles() {
|
|
this.container.innerHTML = '';
|
|
const groupedFiles = this.groupFilesByItem(this.options.existingFiles);
|
|
|
|
Object.keys(groupedFiles).forEach((itemKey, index) => {
|
|
const itemCount = index + 1;
|
|
const newItem = document.createElement("div");
|
|
newItem.classList.add("dynamic-item", "w-full", "mb-4");
|
|
newItem.innerHTML = this.generateItemHTML(itemCount);
|
|
this.container.appendChild(newItem);
|
|
|
|
const previewContainer = newItem.querySelector(".preview-container");
|
|
const fileInput = newItem.querySelector(".file-input");
|
|
|
|
groupedFiles[itemKey].forEach(file => {
|
|
this.addExistingFilePreview(previewContainer, fileInput, file, itemCount);
|
|
});
|
|
|
|
// this.attachEventListeners(newItem);
|
|
});
|
|
|
|
this.updateRemoveButtonVisibility();
|
|
}
|
|
|
|
groupFilesByItem(files) {
|
|
return files.reduce((acc, file) => {
|
|
const itemKey = file.item || '1';
|
|
if (!acc[itemKey]) {
|
|
acc[itemKey] = [];
|
|
}
|
|
acc[itemKey].push(file);
|
|
return acc;
|
|
}, {});
|
|
}
|
|
|
|
addExistingFilePreview(previewContainer, fileInput, file, itemCount) {
|
|
const imgWrapper = document.createElement("div");
|
|
imgWrapper.classList.add("relative", "preview-item");
|
|
|
|
const img = document.createElement("img");
|
|
img.src = file.url || file.path;
|
|
img.classList.add("w-full", "h-auto", "object-cover", "rounded-lg");
|
|
|
|
const removeBtn = document.createElement("button");
|
|
removeBtn.type = "button";
|
|
removeBtn.classList.add("absolute", "top-2", "right-2", "btn", "btn-danger", "btn-sm");
|
|
removeBtn.innerHTML = '<i class="ki-outline ki-cross"></i>';
|
|
|
|
removeBtn.addEventListener("click", () => {
|
|
imgWrapper.remove();
|
|
this.updatePreviewContainerVisibility(previewContainer);
|
|
});
|
|
|
|
imgWrapper.appendChild(img);
|
|
imgWrapper.appendChild(removeBtn);
|
|
previewContainer.appendChild(imgWrapper);
|
|
|
|
this.updatePreviewContainerVisibility(previewContainer);
|
|
}
|
|
|
|
getAllUploadedFiles() {
|
|
const files = [];
|
|
this.container.querySelectorAll('.file-input').forEach(input => {
|
|
if (input.files.length > 0) {
|
|
files.push(...input.files);
|
|
}
|
|
});
|
|
return files;
|
|
}
|
|
}
|
|
</script>
|