// App.js - FTTH Planner Application Logic
// Global variables
let editingItemId = null;
let tempClickLatLng = null;
// Initialize application
$(document).ready(function() {
loadFormData();
initializeEventListeners();
// Add core capacity change listener
$(document).on('change input', '#totalCoreCapacity, #coreUsed', calculateCoreAvailable);
});
// Load form data (tube colors, splitters)
function loadFormData() {
// Load tube colors
$.ajax({
url: 'api/tube_colors.php',
method: 'GET',
success: function(response) {
if (response.success) {
// Populate tube color dropdown
let tubeColorSelect = $('#tubeColor');
tubeColorSelect.empty().append('');
// Populate core color dropdown
let coreColorSelect = $('#coreColor');
coreColorSelect.empty().append('');
response.data.forEach(function(color) {
let option = ``;
tubeColorSelect.append(option);
coreColorSelect.append(option);
});
}
}
});
// Load splitter types
$.ajax({
url: 'api/splitters.php',
method: 'GET',
success: function(response) {
if (response.success) {
let mainSplitterSelect = $('#splitterMain');
let odpSplitterSelect = $('#splitterOdp');
mainSplitterSelect.empty().append('');
odpSplitterSelect.empty().append('');
response.data.forEach(function(splitter) {
let option = ``;
if (splitter.type === 'main') {
mainSplitterSelect.append(option);
} else {
odpSplitterSelect.append(option);
}
});
}
}
});
}
// Initialize event listeners
function initializeEventListeners() {
// Item form submission
$('#itemForm').on('submit', function(e) {
e.preventDefault();
saveItem();
});
// Modal events
$('#itemModal').on('hidden.bs.modal', function() {
resetForm();
});
// Tube color change event to show color preview
$('#tubeColor').on('change', function() {
updateColorPreview();
});
}
// Show add item modal
function showAddItemModal(lat = null, lng = null) {
tempClickLatLng = lat && lng ? {lat: lat, lng: lng} : null;
$('#itemModalTitle').text('Tambah Item FTTH');
$('#itemId').val('');
editingItemId = null;
if (tempClickLatLng) {
$('#itemLat').val(tempClickLatLng.lat);
$('#itemLng').val(tempClickLatLng.lng);
}
$('#itemModal').modal('show');
}
// Add new item (from sidebar)
function addNewItem(itemType) {
showAddItemModal();
// Set item type based on parameter
let itemTypeId = getItemTypeId(itemType);
if (itemTypeId) {
$('#itemType').val(itemTypeId);
}
}
// Get item type ID from name
function getItemTypeId(typeName) {
switch(typeName) {
case 'OLT': return '1';
case 'Tiang Tumpu': return '2';
case 'ODP': return '3';
case 'ODC': return '4';
case 'Pelanggan': return '5';
default: return '';
}
}
// Edit existing item
function editItem(itemId) {
editingItemId = itemId;
$.ajax({
url: 'api/items.php',
method: 'GET',
data: { id: itemId },
success: function(response) {
if (response.success && response.data) {
let item = response.data;
$('#itemModalTitle').text('Edit Item FTTH');
$('#itemId').val(item.id);
$('#itemType').val(item.item_type_id);
$('#itemName').val(item.name);
$('#itemDescription').val(item.description);
$('#itemAddress').val(item.address);
$('#itemLat').val(item.latitude);
$('#itemLng').val(item.longitude);
$('#tubeColor').val(item.tube_color_id);
$('#coreColor').val(item.core_color_id);
$('#cableType').val(item.item_cable_type || 'distribution');
$('#totalCoreCapacity').val(item.total_core_capacity || 24);
$('#coreUsed').val(item.core_used);
$('#splitterMain').val(item.splitter_main_id);
$('#splitterOdp').val(item.splitter_odp_id);
$('#itemStatus').val(item.status);
// Calculate and display core available
setTimeout(() => calculateCoreAvailable(), 100);
updateColorPreview();
$('#itemModal').modal('show');
}
},
error: function() {
showNotification('Error loading item data', 'error');
}
});
}
// Save item (create or update)
function saveItem() {
let method = editingItemId ? 'PUT' : 'POST';
// Validate required fields
if (!$('#itemType').val() || !$('#itemName').val()) {
showNotification('Harap isi semua field yang wajib', 'warning');
return;
}
// If no coordinates provided and not editing, get from temp click
if (!$('#itemLat').val() && !$('#itemLng').val() && tempClickLatLng) {
$('#itemLat').val(tempClickLatLng.lat);
$('#itemLng').val(tempClickLatLng.lng);
}
// Always use POST with FormData for compatibility
let formData = new FormData($('#itemForm')[0]);
// Log original method and current state
console.log('🔧 SAVEITEM DEBUG:');
console.log('Original method:', method);
console.log('editingItemId:', editingItemId);
console.log('Item ID field value:', $('#itemId').val());
// For PUT requests, add _method parameter
if (method === 'PUT') {
formData.append('_method', 'PUT');
// Ensure ID is included for PUT request
if (editingItemId && !formData.get('id')) {
formData.set('id', editingItemId);
}
// Also ensure we have the ID from the hidden field
if ($('#itemId').val() && !formData.get('id')) {
formData.set('id', $('#itemId').val());
}
// Log all data being sent
console.log('🚀 PUT Data being sent (all fields):');
for (let pair of formData.entries()) {
console.log(' ' + pair[0] + ': ' + pair[1]);
}
} else {
console.log('🚀 POST Data being sent (new item)');
}
// Force POST method with explicit type declaration
let requestConfig = {
url: 'api/items.php',
type: 'POST', // Use 'type' instead of 'method' for better compatibility
method: 'POST', // Also set method for newer jQuery versions
data: formData,
processData: false,
contentType: false,
dataType: 'json',
cache: false, // Disable caching
success: function(response) {
if (response && response.success) {
$('#itemModal').modal('hide');
if (editingItemId) {
// Update existing marker
updateMarker(editingItemId, response.data);
showNotification('Item berhasil diupdate', 'success');
} else {
// Add new marker
addMarkerToMap(response.data);
showNotification('Item berhasil ditambahkan', 'success');
}
updateStatistics();
} else {
showNotification(response?.message || 'Error saving item', 'error');
}
},
error: function(xhr, status, error) {
console.error('AJAX Error:', error, xhr.responseText);
console.error('Response Text:', xhr.responseText);
showNotification('Error saving item: ' + error, 'error');
}
};
console.log('🚀 Final request config:', {
url: requestConfig.url,
type: requestConfig.type,
method: requestConfig.method,
dataType: requestConfig.dataType
});
$.ajax(requestConfig);
}
// Update marker on map
function updateMarker(itemId, itemData) {
if (markers[itemId]) {
// Remove old marker
map.removeLayer(markers[itemId]);
delete markers[itemId];
}
// Add updated marker
addMarkerToMap(itemData);
}
// Delete item
function deleteItem(itemId) {
if (confirm('Apakah Anda yakin ingin menghapus item ini?')) {
$.ajax({
url: 'api/items.php',
method: 'DELETE',
data: { id: itemId },
success: function(response) {
if (response.success) {
// Remove marker from map
if (markers[itemId]) {
map.removeLayer(markers[itemId]);
delete markers[itemId];
}
// Remove any routes connected to this item
removeRoutesForItem(itemId);
showNotification('Item berhasil dihapus', 'success');
updateStatistics();
} else {
showNotification(response.message || 'Error deleting item', 'error');
}
},
error: function() {
showNotification('Error deleting item', 'error');
}
});
}
}
// Remove routes connected to item
function removeRoutesForItem(itemId) {
$.ajax({
url: 'api/routes.php',
method: 'DELETE',
data: { item_id: itemId },
success: function(response) {
if (response.success && response.deleted_routes) {
response.deleted_routes.forEach(function(routeId) {
if (routes[routeId]) {
map.removeLayer(routes[routeId]);
delete routes[routeId];
}
});
}
}
});
}
// Reset form
function resetForm() {
$('#itemForm')[0].reset();
$('#itemId').val('');
editingItemId = null;
tempClickLatLng = null;
updateColorPreview();
}
// Update color preview
function updateColorPreview() {
let selectedColor = $('#tubeColor option:selected').data('color');
if (selectedColor) {
$('#tubeColor').css('border-left', `5px solid ${selectedColor}`);
} else {
$('#tubeColor').css('border-left', 'none');
}
}
// Show item list
function showItemList() {
$.ajax({
url: 'api/items.php',
method: 'GET',
success: function(response) {
if (response.success) {
let itemListHtml = generateItemListHtml(response.data);
showModal('Daftar Item FTTH', itemListHtml, 'modal-xl');
}
},
error: function() {
showNotification('Error loading item list', 'error');
}
});
}
// Generate item list HTML
function generateItemListHtml(items) {
let html = `
Jenis
Nama
Alamat
Koordinat
Status
Aksi
`;
items.forEach(function(item) {
html += `
${item.item_type_name}
${item.name}
${item.address || '-'}
${(isNaN(parseFloat(item.latitude)) || isNaN(parseFloat(item.longitude))) ? 'Koordinat tidak valid' : `${parseFloat(item.latitude).toFixed(6)}, ${parseFloat(item.longitude).toFixed(6)}`}
${getStatusText(item.status)}
`;
});
html += `
`;
return html;
}
// Get item color
function getItemColor(typeName) {
switch(typeName) {
case 'OLT': return '#FF6B6B';
case 'Tiang Tumpu': return '#4ECDC4';
case 'ODP': return '#45B7D1';
case 'ODC': return '#96CEB4';
case 'Pelanggan': return '#FFA500';
default: return '#999';
}
}
// Focus on item in map
function focusOnItem(itemId) {
if (markers[itemId]) {
let marker = markers[itemId];
map.setView(marker.getLatLng(), 16);
marker.openPopup();
}
}
// Show route list
function showRouteList() {
$.ajax({
url: 'api/routes.php',
method: 'GET',
success: function(response) {
if (response.success) {
let routeListHtml = generateRouteListHtml(response.data);
showModal('Daftar Routing Kabel', routeListHtml, 'modal-xl');
}
},
error: function() {
showNotification('Error loading route list', 'error');
}
});
}
// Generate route list HTML
function generateRouteListHtml(routes) {
let html = `
Dari
Ke
Jarak
Tipe Kabel
Core
Status
Aksi
`;
routes.forEach(function(route) {
let distance = route.distance ? (route.distance / 1000).toFixed(2) + ' km' : '-';
html += `
${route.from_item_name || 'Unknown'}
${route.to_item_name || 'Unknown'}
${distance}
${route.cable_type || '-'}
${route.core_count || '-'}
${getStatusText(route.status)}
`;
});
html += `
`;
return html;
}
// Focus on route in map
function focusOnRoute(routeId) {
if (routes[routeId]) {
let route = routes[routeId];
map.fitBounds(route.getBounds());
route.openPopup();
}
}
// Delete route
function deleteRoute(routeId) {
if (confirm('Apakah Anda yakin ingin menghapus route ini?')) {
$.ajax({
url: 'api/routes.php',
method: 'DELETE',
data: { id: routeId },
success: function(response) {
if (response.success) {
if (routes[routeId]) {
map.removeLayer(routes[routeId]);
delete routes[routeId];
}
showNotification('Route berhasil dihapus', 'success');
} else {
showNotification(response.message || 'Error deleting route', 'error');
}
},
error: function() {
showNotification('Error deleting route', 'error');
}
});
}
}
// Generic modal function
function showModal(title, content, size = 'modal-lg') {
if (!$('#genericModal').length) {
$('body').append(`
`);
}
$('#genericModalTitle').text(title);
$('#genericModalBody').html(content);
$('#genericModal').modal('show');
}
// Edit route function
function editRoute(routeId) {
// Get route data first
$.ajax({
url: 'api/routes.php',
method: 'GET',
data: { id: routeId },
success: function(response) {
if (response.success && response.data) {
let route = response.data;
showEditRouteModal(route);
} else {
showNotification('Error loading route data', 'error');
}
},
error: function() {
showNotification('Error loading route data', 'error');
}
});
}
//terpasang kabel rx power
// Contoh marker pelanggan
// Event saat dropdown status diubah
// Fungsi untuk memuat RX Power di tengah kabel
// Show edit route modal
function showEditRouteModal(route) {
let modalHtml = `
`;
// Create modal if doesn't exist
if (!$('#routeEditModal').length) {
$('body').append(`
Edit Routing Kabel
`);
}
$('#routeEditModalBody').html(modalHtml);
$('#routeEditModal').modal('show');
// Handle form submission
$('#editRouteForm').on('submit', function(e) {
e.preventDefault();
saveRouteEdit();
});
}
// Save route edit
function saveRouteEdit() {
let formData = new FormData();
formData.append('_method', 'PUT');
formData.append('id', $('#editRouteId').val());
formData.append('cable_type', $('#editCableType').val());
formData.append('core_count', $('#editCoreCount').val());
formData.append('status', $('#editRouteStatus').val());
console.log('🚀 Route Edit Data being sent:');
for (let pair of formData.entries()) {
console.log(' ' + pair[0] + ': ' + pair[1]);
}
$.ajax({
url: 'api/routes.php',
type: 'POST',
method: 'POST',
data: formData,
processData: false,
contentType: false,
dataType: 'json',
cache: false,
success: function(response) {
console.log('✅ Route update response:', response);
if (response.success) {
$('#routeEditModal').modal('hide');
showNotification('Route berhasil diupdate', 'success');
// Refresh route list if open
if ($('#genericModal').hasClass('show')) {
showRouteList();
}
// Update route on map
loadRoutes();
} else {
console.error('❌ Route update failed:', response.message);
showNotification(response.message || 'Error updating route', 'error');
}
},
error: function(xhr, status, error) {
console.error('❌ AJAX Error:', error, xhr.responseText);
console.error('Response Text:', xhr.responseText);
try {
let errorResponse = JSON.parse(xhr.responseText);
showNotification(errorResponse.message || 'Error updating route', 'error');
} catch(e) {
showNotification('Error updating route: ' + error, 'error');
}
}
});
}
// Calculate core available
function calculateCoreAvailable() {
let totalCapacity = parseInt($('#totalCoreCapacity').val()) || 0;
let coreUsed = parseInt($('#coreUsed').val()) || 0;
let coreAvailable = totalCapacity - coreUsed;
$('#coreAvailable').val(coreAvailable + ' / ' + totalCapacity + ' Core');
// Set color based on availability
if (coreAvailable <= 0) {
$('#coreAvailable').removeClass('text-success text-warning').addClass('text-danger');
} else if (coreAvailable <= totalCapacity * 0.2) {
$('#coreAvailable').removeClass('text-success text-danger').addClass('text-warning');
} else {
$('#coreAvailable').removeClass('text-danger text-warning').addClass('text-success');
}
}
// Sync core usage from routes
function syncCoreUsageFromRoutes(itemId) {
if (!itemId) return;
$.ajax({
url: 'api/routes.php',
method: 'GET',
success: function(response) {
if (response.success) {
let totalCoreUsed = 0;
response.data.forEach(function(route) {
if (route.from_item_id == itemId || route.to_item_id == itemId) {
totalCoreUsed += parseInt(route.core_count) || 0;
}
});
// Update core used in form
$('#coreUsed').val(totalCoreUsed);
calculateCoreAvailable();
console.log(`📊 Core usage synced for item ${itemId}: ${totalCoreUsed} cores used`);
}
},
error: function() {
console.error('Failed to sync core usage from routes');
}
});
}
// Enhanced edit item to include core sync
function editItemEnhanced(itemId) {
editItem(itemId);
// Sync core usage after loading item data
setTimeout(() => syncCoreUsageFromRoutes(itemId), 500);
}
// Show item detail
function showItemDetail(itemId) {
$.ajax({
url: 'api/items.php',
method: 'GET',
data: { id: itemId },
success: function(response) {
if (response.success && response.data) {
let item = response.data;
showItemDetailModal(item);
} else {
showNotification('Error loading item data', 'error');
}
},
error: function() {
showNotification('Error loading item data', 'error');
}
});
}
// Show item detail modal //ini detail jika mau edit
function showItemDetailModal(item) {
let modalHtml = `
`;
// Create modal if doesn't exist
if (!$('#itemDetailModal').length) {
$('body').append(`
Detail Item FTTH
`);
}
$('#itemDetailModalBody').html(modalHtml);
$('#itemDetailModal').modal('show');
}
//detailredaman
// Fungsi untuk menampilkan detail redaman
function showRedamanDetail(item) {
let modalHtml = `