Refactor: Move app to src/, update Dockerfile and detailed README
All checks were successful
Docker Build & Push / build-and-push (push) Successful in 31s

This commit is contained in:
Gemini Bot
2025-12-07 17:12:22 +00:00
parent 61ede4c325
commit 5880593831
34 changed files with 42 additions and 25 deletions

97
src/js/main.js Normal file
View File

@@ -0,0 +1,97 @@
$(document).ready(function() {
// Bootstrap Initialisierungen
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
// DataTables Initialisierungen
if ($('#seeds-table').length) {
$('#seeds-table').DataTable({ "dom": "t", "paging": false, "info": false, "language": { "zeroRecords": "Keine passenden Samen gefunden" }, "columnDefs": [ { "orderable": false, "targets": 'no-sort' } ] });
}
if ($('#plants-table').length) {
$('#plants-table').DataTable({ "dom": "t", "paging": false, "info": false, "order": [[ 4, "desc" ]], "language": { "zeroRecords": "Keine passenden Pflanzen gefunden" }, "columnDefs": [ { "orderable": false, "targets": 'no-sort' }, { "type": "num", "targets": 4 } ] });
}
// Universeller Formular-Handler
function handleFormSubmit(form) {
$.ajax({
type: 'POST', url: 'ajax_handler.php', data: form.serialize(), dataType: 'json',
success: function(response) {
if (response.success) {
if (response.message) { alert(response.message); }
location.reload();
} else { alert('Speichern fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); }
},
error: () => alert('Ein schwerwiegender Serverfehler ist aufgetreten.')
});
}
// Event-Listener für alle Standard-Formulare
$('form:not(#image-upload-form, #change-password-form)').on('submit', function(e) {
e.preventDefault();
if ($(this).attr('id') === 'generate-api-key-form' && !confirm('Bist du sicher? Ein neuer Key macht einen eventuell bestehenden ungültig.')) { return; }
handleFormSubmit($(this));
});
// Spezielle Handler für Datei-Upload und Passwort-Änderung
$('#image-upload-form').on('submit', function(e) { e.preventDefault(); const formData = new FormData(this); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: formData, dataType: 'json', contentType: false, processData: false, success: function(response) { if (response.success) { location.reload(); } else { alert('Fehler beim Upload: ' + (response.message || 'Unbekannter Fehler')); } }, error: function() { alert('Ein schwerwiegender Serverfehler ist aufgetreten.'); } }); });
$('#change-password-form').on('submit', function(e) { e.preventDefault(); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: $(this).serialize(), dataType: 'json', success: function(response) { if (response.success) { $('#changePasswordModal').modal('hide'); alert(response.message); } else { alert('Fehler: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
// Modal-Logik (Befüllen von Formularen beim Öffnen)
$('#zoneModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#zone-form'); form.trigger('reset'); if (button.data('id')) { $('#zoneModalLabel').text('Zone bearbeiten'); form.find('[name="name"]').val(button.data('name')); form.find('[name="id"]').val(button.data('id')); form.find('[name="action"]').val('edit_zone'); } else { $('#zoneModalLabel').text('Neue Zone hinzufügen'); form.find('[name="action"]').val('add_zone'); } });
$('#containerModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#container-form'); form.trigger('reset'); if (button.data('id')) { $('#containerModalLabel').text('Pflanzgefäß bearbeiten'); form.find('[name="name"]').val(button.data('name')); form.find('[name="id"]').val(button.data('id')); form.find('[name="zone_id"]').val(button.data('zoneid')); form.find('[name="action"]').val('edit_container'); } else { $('#containerModalLabel').text('Neues Pflanzgefäß hinzufügen'); form.find('[name="action"]').val('add_container'); } });
$('#seedModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#seed-form'); form.trigger('reset'); $('#ratio_sativa').val(50).trigger('input'); if (button.data('seed')) { $('#seedModalLabel').text('Samen bearbeiten'); const seedData = button.data('seed'); form.find('[name="id"]').val(seedData.id); form.find('[name="strain_name"]').val(seedData.strain_name); form.find('[name="internal_name"]').val(seedData.internal_name); form.find('[name="stock_count"]').val(seedData.stock_count); form.find('[name="info_url"]').val(seedData.info_url); form.find('[name="description"]').val(seedData.description); form.find('[name="ratio_sativa"]').val(seedData.ratio_sativa).trigger('input'); form.find('[name="is_autoflower"]').prop('checked', seedData.is_autoflower == 1); form.find('[name="action"]').val('edit_seed'); } else { $('#seedModalLabel').text('Neuen Samen hinzufügen'); form.find('[name="action"]').val('add_seed'); } });
$('#editPlantModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#edit-plant-form'); const plantData = button.data('plant-info'); form.find('[name="phase"]').val(plantData.phase); form.find('[name="plant_date"]').val(plantData.plant_date); form.find('[name="zone_id"]').val(plantData.zone_id).trigger('change', [plantData.container_id]); });
$('#addActivityModal').on('show.bs.modal', function() { const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); });
$('#addMeasurementModal').on('show.bs.modal', function() { const today = new Date().toISOString().slice(0, 10); $(this).find('input[type="date"]').val(today); });
// Logik für abhängige Dropdowns
$('#zone-select, #edit-zone-select').on('change', function(event, preselectContainerId) { const zoneId = $(this).val(); const isEditMode = $(this).attr('id') === 'edit-zone-select'; const containerSelect = isEditMode ? $('#edit-container-select') : $('#container-select'); containerSelect.prop('disabled', true).html('<option value="">Lade Gefäße...</option>'); if (!zoneId) { containerSelect.html('<option value="">Zuerst Zone wählen</option>'); return; } $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_containers_by_zone', zone_id: zoneId }, dataType: 'json', success: function(response) { if (response.success) { containerSelect.empty().append('<option value="">Pflanzgefäß wählen</option>'); if (isEditMode && preselectContainerId) { const plantData = $('.edit-plant-btn').data('plant-info'); containerSelect.append($('<option>', { value: plantData.container_id, text: plantData.container_name, selected: true })); } if (response.data.length > 0) { response.data.forEach(function(container) { if (!isEditMode || container.id != preselectContainerId) { containerSelect.append($('<option>', { value: container.id, text: container.name })); } }); } if(preselectContainerId) { containerSelect.val(preselectContainerId); } containerSelect.prop('disabled', false); } else { containerSelect.html('<option value="">Fehler</option>'); } }, error: function() { containerSelect.html('<option value="">Serverfehler!</option>'); } }); });
// Logik für Status-Änderungen und Löschen
$('#confirmHarvestBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'harvest_plant', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#confirmFinishDryingBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'finish_drying', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
let itemToDelete = { id: null, type: null };
$('#deleteConfirmModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); itemToDelete.id = button.data('id'); itemToDelete.type = button.data('type'); $('#item-name-to-delete').text(button.data('name')); let typeText = 'dieses Element'; if (itemToDelete.type === 'zone') { typeText = 'die Zone'; } else if (itemToDelete.type === 'container') { typeText = 'das Pflanzgefäß'; } else if (itemToDelete.type === 'seed') { typeText = 'diesen Samen'; } else if (itemToDelete.type === 'plant') { typeText = 'diese Pflanze'; } else if (itemToDelete.type === 'plant_image') { typeText = 'dieses Bild'; } $('#item-type-to-delete').text(typeText); $('#delete-warning').toggle(itemToDelete.type === 'zone'); });
$('#confirmDeleteBtn').on('click', function() { if (!itemToDelete.id || !itemToDelete.type) return; $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'delete_' + itemToDelete.type, id: itemToDelete.id }, dataType: 'json', success: function(response) { if (response.success) { if (itemToDelete.type === 'plant') { window.location.href = 'plants.php'; } else { location.reload(); } } else { alert('Löschen fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
// Globale Aktivitäten Logik
const globalActivityModal = document.getElementById('globalActivityModal');
if (globalActivityModal) { globalActivityModal.addEventListener('show.bs.modal', function () { const zoneFilterSelect = $('#zone-filter-select'); if(zoneFilterSelect.children().length <= 1) { $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_all_zones' }, dataType: 'json', success: function(response) { if (response.success && response.data.length > 0) { response.data.forEach(function(zone) { zoneFilterSelect.append($('<option>', { value: zone.id, text: zone.name })); }); } } }); } const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); zoneFilterSelect.val('all').trigger('change'); }); }
$('#zone-filter-select').on('change', function() { const zoneId = $(this).val(); const selectionContainer = $('#plant-target-selection'); selectionContainer.html('<div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">Lade...</span></div>'); $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_plants_by_zone', zone_id: zoneId }, dataType: 'json', success: function(response) { selectionContainer.empty(); if (response.success && response.data.length > 0) { let plantCheckboxes = '<div class="form-check"><input class="form-check-input" type="checkbox" id="select-all-plants" checked><label class="form-check-label fw-bold" for="select-all-plants">Alle auswählen/abwählen</label></div><hr class="my-2">'; plantCheckboxes += '<div class="row" style="max-height: 200px; overflow-y: auto;">'; response.data.forEach(function(plant) { plantCheckboxes += `<div class="col-md-6"><div class="form-check"><input class="form-check-input plant-checkbox" type="checkbox" name="plant_ids[]" value="${plant.id}" id="plant_${plant.id}" checked><label class="form-check-label" for="plant_${plant.id}">${plant.strain_name} (${plant.container_name})</label></div></div>`; }); plantCheckboxes += '</div>'; selectionContainer.html(plantCheckboxes); } else { selectionContainer.html('<p class="text-muted">Keine aktiven Pflanzen in dieser Auswahl gefunden.</p>'); } } }); });
$(document).on('change', '#select-all-plants', function() { $('#global-activity-form .plant-checkbox').prop('checked', $(this).prop('checked')); });
// Sensor-Graphen Logik
$('#sensor-tab-btn').on('shown.bs.tab', function () {
if ($(this).data('loaded')) { return; }
$(this).data('loaded', true);
const plantId = new URLSearchParams(window.location.search).get('id');
const chartsContainer = $('#sensor-charts-container');
chartsContainer.html('<div class="col-12 text-center p-5"><div class="spinner-border text-secondary" role="status"><span class="visually-hidden">Lade...</span></div></div>');
$.ajax({
type: 'GET', url: 'ajax_handler.php', data: { action: 'get_sensor_data', plant_id: plantId }, dataType: 'json',
success: function(response) {
chartsContainer.empty();
if (response.success && response.data.labels.length > 0) {
chartsContainer.html('<div class="col-lg-6 mb-4" id="temp-chart-wrapper"></div><div class="col-lg-6 mb-4" id="humidity-chart-wrapper"></div>');
const tempWrapper = $('#temp-chart-wrapper');
const humidityWrapper = $('#humidity-chart-wrapper');
if (response.data.temperature.some(val => val !== null)) {
tempWrapper.append('<h5>Temperaturverlauf</h5>');
let tempCanvas = $('<canvas>').attr('height', '300');
tempWrapper.append($('<div>').addClass('cazubu-table-frameless p-2').append(tempCanvas));
new Chart(tempCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Temperatur (°C)', data: response.data.temperature, borderColor: 'rgba(255, 99, 132, 1)', backgroundColor: 'rgba(255, 99, 132, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false, scales: { x: { ticks: { color: '#212529' } }, y: { ticks: { color: '#212529' } } }, plugins: { legend: { labels: { color: '#212529' } } } } });
} else { tempWrapper.append('<div class="cazubu-table-frameless p-3 text-center">Keine Temperaturdaten vorhanden.</div>'); }
if (response.data.humidity.some(val => val !== null)) {
humidityWrapper.append('<h5>Feuchtigkeitsverlauf</h5>');
let humidityCanvas = $('<canvas>').attr('height', '300');
humidityWrapper.append($('<div>').addClass('cazubu-table-frameless p-2').append(humidityCanvas));
new Chart(humidityCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Feuchtigkeit (%)', data: response.data.humidity, borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false, scales: { x: { ticks: { color: '#212529' } }, y: { ticks: { color: '#212529' } } }, plugins: { legend: { labels: { color: '#212529' } } } } });
} else { humidityWrapper.append('<div class="cazubu-table-frameless p-3 text-center">Keine Feuchtigkeitsdaten vorhanden.</div>'); }
} else { chartsContainer.html('<div class="col-12"><div class="cazubu-table-frameless p-3 text-center">Keine Sensordaten für diese Pflanze vorhanden.</div></div>'); }
},
error: function() { chartsContainer.html('<div class="col-12"><div class="cazubu-table-frameless p-3 text-center text-danger">Fehler beim Laden der Sensordaten.</div></div>'); }
});
});
});

90
src/js/main.js.0629Uhr Normal file
View File

@@ -0,0 +1,90 @@
$(document).ready(function() {
// Bootstrap Initialisierungen
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
// DataTables für die Samen-Tabelle initialisieren
if ($('#seeds-table').length) {
$('#seeds-table').DataTable({
"dom": "t", // 't' bedeutet, dass NUR die Tabelle gerendert wird, ohne extra Controls
"paging": false,
"info": false,
"language": {
"zeroRecords": "Keine passenden Samen gefunden",
},
"columnDefs": [
{ "orderable": false, "targets": 'no-sort' }
]
});
}
// Zonen Logik
$('#zoneModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#zone-form'); form.trigger('reset'); if (button.data('id')) { $('#zoneModalLabel').text('Zone bearbeiten'); form.find('[name="name"]').val(button.data('name')); form.find('[name="id"]').val(button.data('id')); form.find('[name="action"]').val('edit_zone'); } else { $('#zoneModalLabel').text('Neue Zone hinzufügen'); form.find('[name="action"]').val('add_zone'); } });
$('#zone-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
// Pflanzgefäß Logik
$('#containerModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#container-form'); form.trigger('reset'); if (button.data('id')) { $('#containerModalLabel').text('Pflanzgefäß bearbeiten'); form.find('[name="name"]').val(button.data('name')); form.find('[name="id"]').val(button.data('id')); form.find('[name="zone_id"]').val(button.data('zoneid')); form.find('[name="action"]').val('edit_container'); } else { $('#containerModalLabel').text('Neues Pflanzgefäß hinzufügen'); form.find('[name="action"]').val('add_container'); } });
$('#container-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
// Samen Logik
$('#seedModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#seed-form'); form.trigger('reset'); $('#ratio_sativa').val(50).trigger('input'); if (button.data('seed')) { $('#seedModalLabel').text('Samen bearbeiten'); const seedData = button.data('seed'); form.find('[name="id"]').val(seedData.id); form.find('[name="strain_name"]').val(seedData.strain_name); form.find('[name="internal_name"]').val(seedData.internal_name); form.find('[name="stock_count"]').val(seedData.stock_count); form.find('[name="info_url"]').val(seedData.info_url); form.find('[name="description"]').val(seedData.description); form.find('[name="ratio_sativa"]').val(seedData.ratio_sativa).trigger('input'); form.find('[name="is_autoflower"]').prop('checked', seedData.is_autoflower == 1); form.find('[name="action"]').val('edit_seed'); } else { $('#seedModalLabel').text('Neuen Samen hinzufügen'); form.find('[name="action"]').val('add_seed'); } });
$('#seed-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#ratio_sativa').on('input', function() { let sativa = $(this).val(); $('#sativa-value-label').text(sativa); $('#indica-value-label').text(100 - sativa); });
// Pflanzen Logik
$('#plant-form, #edit-plant-form, #activity-form, #measurement-form, #global-activity-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#image-upload-form').on('submit', function(e) { e.preventDefault(); const formData = new FormData(this); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: formData, dataType: 'json', contentType: false, processData: false, success: function(response) { if (response.success) { location.reload(); } else { alert('Fehler beim Upload: ' + (response.message || 'Unbekannter Fehler')); } }, error: function() { alert('Ein schwerwiegender Serverfehler ist aufgetreten.'); } }); });
$('#editPlantModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#edit-plant-form'); const plantData = button.data('plant-info'); form.find('[name="phase"]').val(plantData.phase); form.find('[name="plant_date"]').val(plantData.plant_date); form.find('[name="zone_id"]').val(plantData.zone_id).trigger('change', [plantData.container_id]); });
$('#zone-select, #edit-zone-select').on('change', function(event, preselectContainerId) { const zoneId = $(this).val(); const isEditMode = $(this).attr('id') === 'edit-zone-select'; const containerSelect = isEditMode ? $('#edit-container-select') : $('#container-select'); containerSelect.prop('disabled', true).html('<option value="">Lade Gefäße...</option>'); if (!zoneId) { containerSelect.html('<option value="">Zuerst Zone wählen</option>'); return; } $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_containers_by_zone', zone_id: zoneId }, dataType: 'json', success: function(response) { if (response.success) { containerSelect.empty().append('<option value="">Pflanzgefäß wählen</option>'); if (isEditMode && preselectContainerId) { const plantData = $('.edit-plant-btn').data('plant-info'); containerSelect.append($('<option>', { value: plantData.container_id, text: plantData.container_name })); } if (response.data.length > 0) { response.data.forEach(function(container) { if (!isEditMode || container.id != preselectContainerId) { containerSelect.append($('<option>', { value: container.id, text: container.name })); } }); } if(preselectContainerId) { containerSelect.val(preselectContainerId); } containerSelect.prop('disabled', false); } else { containerSelect.html('<option value="">Fehler</option>'); } }, error: function() { containerSelect.html('<option value="">Serverfehler!</option>'); } }); });
$('#confirmHarvestBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'harvest_plant', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#confirmFinishDryingBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'finish_drying', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#addActivityModal').on('show.bs.modal', function() { const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); });
$('#addMeasurementModal').on('show.bs.modal', function() { const today = new Date().toISOString().slice(0, 10); $(this).find('input[type="date"]').val(today); });
// Profil-Seiten Logik
$('#generate-api-key-form').on('submit', function(e) { e.preventDefault(); if (confirm('Bist du sicher? Ein neuer Key macht einen eventuell bestehenden ungültig.')) { handleFormSubmit($(this)); } });
$('#change-username-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#change-password-form').on('submit', function(e) { e.preventDefault(); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: $(this).serialize(), dataType: 'json', success: function(response) { if (response.success) { $('#changePasswordModal').modal('hide'); alert(response.message); } else { alert('Fehler: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
// Sensor-Graphen Logik
$('#sensor-tab-btn').on('shown.bs.tab', function () {
if ($(this).data('loaded')) { return; }
$(this).data('loaded', true);
const plantId = new URLSearchParams(window.location.search).get('id');
const chartsContainer = $('#sensor-charts-container');
chartsContainer.html('<div class="col-12 text-center p-5"><div class="spinner-border text-secondary" role="status"><span class="visually-hidden">Lade...</span></div></div>');
$.ajax({
type: 'GET', url: 'ajax_handler.php', data: { action: 'get_sensor_data', plant_id: plantId }, dataType: 'json',
success: function(response) {
chartsContainer.empty();
if (response.success && response.data.labels.length > 0) {
chartsContainer.html('<div class="col-lg-6" id="temp-chart-wrapper"></div><div class="col-lg-6" id="humidity-chart-wrapper"></div>');
const tempWrapper = $('#temp-chart-wrapper'); const humidityWrapper = $('#humidity-chart-wrapper');
if (response.data.temperature.some(val => val !== null)) {
tempWrapper.append('<h5>Temperaturverlauf</h5>'); let tempCanvas = $('<canvas>').attr('height', '300'); tempWrapper.append($('<div>').addClass('mb-4').append(tempCanvas));
new Chart(tempCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Temperatur (°C)', data: response.data.temperature, borderColor: 'rgba(255, 99, 132, 1)', backgroundColor: 'rgba(255, 99, 132, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false } });
} else { tempWrapper.append('<p class="text-muted">Keine Temperaturdaten vorhanden.</p>'); }
if (response.data.humidity.some(val => val !== null)) {
humidityWrapper.append('<h5>Feuchtigkeitsverlauf</h5>'); let humidityCanvas = $('<canvas>').attr('height', '300'); humidityWrapper.append($('<div>').addClass('mb-4').append(humidityCanvas));
new Chart(humidityCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Feuchtigkeit (%)', data: response.data.humidity, borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false } });
} else { humidityWrapper.append('<p class="text-muted">Keine Feuchtigkeitsdaten vorhanden.</p>'); }
} else { chartsContainer.html('<div class="col-12"><p class="text-muted">Keine Sensordaten für diese Pflanze vorhanden.</p></div>'); }
},
error: function() { chartsContainer.html('<div class="col-12"><p class="text-danger">Fehler beim Laden der Sensordaten.</p></div>'); }
});
});
// Globale Aktivitäten Logik
const globalActivityModal = document.getElementById('globalActivityModal');
if (globalActivityModal) { globalActivityModal.addEventListener('show.bs.modal', function () { const zoneFilterSelect = $('#zone-filter-select'); if(zoneFilterSelect.children().length <= 1) { $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_all_zones' }, dataType: 'json', success: function(response) { if (response.success && response.data.length > 0) { response.data.forEach(function(zone) { zoneFilterSelect.append($('<option>', { value: zone.id, text: zone.name })); }); } } }); } const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); zoneFilterSelect.val('all').trigger('change'); }); }
$('#zone-filter-select').on('change', function() { const zoneId = $(this).val(); const selectionContainer = $('#plant-target-selection'); selectionContainer.html('<div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">Lade...</span></div>'); $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_plants_by_zone', zone_id: zoneId }, dataType: 'json', success: function(response) { selectionContainer.empty(); if (response.success && response.data.length > 0) { let plantCheckboxes = '<div class="form-check"><input class="form-check-input" type="checkbox" id="select-all-plants" checked><label class="form-check-label fw-bold" for="select-all-plants">Alle auswählen/abwählen</label></div><hr class="my-2">'; plantCheckboxes += '<div class="row" style="max-height: 200px; overflow-y: auto;">'; response.data.forEach(function(plant) { plantCheckboxes += `<div class="col-md-6"><div class="form-check"><input class="form-check-input plant-checkbox" type="checkbox" name="plant_ids[]" value="${plant.id}" id="plant_${plant.id}" checked><label class="form-check-label" for="plant_${plant.id}">${plant.strain_name} (${plant.container_name})</label></div></div>`; }); plantCheckboxes += '</div>'; selectionContainer.html(plantCheckboxes); } else { selectionContainer.html('<p class="text-muted">Keine aktiven Pflanzen in dieser Auswahl gefunden.</p>'); } } }); });
$(document).on('change', '#select-all-plants', function() { $('#global-activity-form .plant-checkbox').prop('checked', $(this).prop('checked')); });
// Universelle Logik
let itemToDelete = { id: null, type: null };
$('#deleteConfirmModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); itemToDelete.id = button.data('id'); itemToDelete.type = button.data('type'); $('#item-name-to-delete').text(button.data('name')); let typeText = 'dieses Element'; if (itemToDelete.type === 'zone') { typeText = 'die Zone'; } else if (itemToDelete.type === 'container') { typeText = 'das Pflanzgefäß'; } else if (itemToDelete.type === 'seed') { typeText = 'diesen Samen'; } else if (itemToDelete.type === 'plant') { typeText = 'diese Pflanze'; } else if (itemToDelete.type === 'plant_image') { typeText = 'dieses Bild'; } $('#item-type-to-delete').text(typeText); $('#delete-warning').toggle(itemToDelete.type === 'zone'); });
$('#confirmDeleteBtn').on('click', function() { if (!itemToDelete.id || !itemToDelete.type) return; $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'delete_' + itemToDelete.type, id: itemToDelete.id }, dataType: 'json', success: function(response) { if (response.success) { if (itemToDelete.type === 'plant') { window.location.href = 'plants.php'; } else { location.reload(); } } else { alert('Löschen fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
function handleFormSubmit(form) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: form.serialize(), dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Speichern fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); }
});

78
src/js/main.js.1310Uhr Normal file
View File

@@ -0,0 +1,78 @@
$(document).ready(function() {
// Bootstrap Initialisierungen
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
// Zonen Logik
$('#zoneModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#zone-form'); form.trigger('reset'); if (button.data('id')) { $('#zoneModalLabel').text('Zone bearbeiten'); form.find('[name="name"]').val(button.data('name')); form.find('[name="id"]').val(button.data('id')); form.find('[name="action"]').val('edit_zone'); } else { $('#zoneModalLabel').text('Neue Zone hinzufügen'); form.find('[name="action"]').val('add_zone'); } });
$('#zone-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
// Pflanzgefäß Logik
$('#containerModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#container-form'); form.trigger('reset'); if (button.data('id')) { $('#containerModalLabel').text('Pflanzgefäß bearbeiten'); form.find('[name="name"]').val(button.data('name')); form.find('[name="id"]').val(button.data('id')); form.find('[name="zone_id"]').val(button.data('zoneid')); form.find('[name="action"]').val('edit_container'); } else { $('#containerModalLabel').text('Neues Pflanzgefäß hinzufügen'); form.find('[name="action"]').val('add_container'); } });
$('#container-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
// Samen Logik
$('#seedModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#seed-form'); form.trigger('reset'); $('#ratio_sativa').val(50).trigger('input'); if (button.data('seed')) { $('#seedModalLabel').text('Samen bearbeiten'); const seedData = button.data('seed'); form.find('[name="id"]').val(seedData.id); form.find('[name="strain_name"]').val(seedData.strain_name); form.find('[name="internal_name"]').val(seedData.internal_name); form.find('[name="stock_count"]').val(seedData.stock_count); form.find('[name="info_url"]').val(seedData.info_url); form.find('[name="description"]').val(seedData.description); form.find('[name="ratio_sativa"]').val(seedData.ratio_sativa).trigger('input'); form.find('[name="is_autoflower"]').prop('checked', seedData.is_autoflower == 1); form.find('[name="action"]').val('edit_seed'); } else { $('#seedModalLabel').text('Neuen Samen hinzufügen'); form.find('[name="action"]').val('add_seed'); } });
$('#seed-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#ratio_sativa').on('input', function() { let sativa = $(this).val(); $('#sativa-value-label').text(sativa); $('#indica-value-label').text(100 - sativa); });
// Pflanzen Logik
$('#plant-form, #edit-plant-form, #activity-form, #measurement-form, #global-activity-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#image-upload-form').on('submit', function(e) { e.preventDefault(); const formData = new FormData(this); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: formData, dataType: 'json', contentType: false, processData: false, success: function(response) { if (response.success) { location.reload(); } else { alert('Fehler beim Upload: ' + (response.message || 'Unbekannter Fehler')); } }, error: function() { alert('Ein schwerwiegender Serverfehler ist aufgetreten.'); } }); });
$('#editPlantModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#edit-plant-form'); const plantData = button.data('plant-info'); form.find('[name="phase"]').val(plantData.phase); form.find('[name="plant_date"]').val(plantData.plant_date); form.find('[name="zone_id"]').val(plantData.zone_id).trigger('change', [plantData.container_id]); });
$('#zone-select, #edit-zone-select').on('change', function(event, preselectContainerId) { const zoneId = $(this).val(); const containerSelect = $(this).attr('id') === 'zone-select' ? $('#container-select') : $('#edit-container-select'); containerSelect.prop('disabled', true).html('<option value="">Lade Gefäße...</option>'); if (!zoneId) { containerSelect.html('<option value="">Zuerst Zone wählen</option>'); return; } $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_containers_by_zone', zone_id: zoneId }, dataType: 'json', success: function(response) { if (response.success) { containerSelect.empty().append('<option value="">Pflanzgefäß wählen</option>'); if (isEditMode && preselectContainerId) { const plantData = $('.edit-plant-btn').data('plant-info'); containerSelect.append($('<option>', { value: plantData.container_id, text: plantData.container_name })); } if (response.data.length > 0) { response.data.forEach(function(container) { if (!isEditMode || container.id != preselectContainerId) { containerSelect.append($('<option>', { value: container.id, text: container.name })); } }); } if(preselectContainerId) { containerSelect.val(preselectContainerId); } containerSelect.prop('disabled', false); } else { containerSelect.html('<option value="">Fehler</option>'); } }, error: function() { containerSelect.html('<option value="">Serverfehler!</option>'); } }); });
$('#confirmHarvestBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'harvest_plant', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#confirmFinishDryingBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'finish_drying', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#addActivityModal').on('show.bs.modal', function() { const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); });
$('#addMeasurementModal').on('show.bs.modal', function() { const today = new Date().toISOString().slice(0, 10); $(this).find('input[type="date"]').val(today); });
// Profil-Seiten Logik
$('#generate-api-key-form').on('submit', function(e) { e.preventDefault(); if (confirm('Bist du sicher? Ein neuer Key macht einen eventuell bestehenden ungültig.')) { handleFormSubmit($(this)); } });
$('#change-username-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#change-password-form').on('submit', function(e) { e.preventDefault(); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: $(this).serialize(), dataType: 'json', success: function(response) { if (response.success) { $('#changePasswordModal').modal('hide'); alert(response.message); } else { alert('Fehler: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
// Sensor-Graphen Logik
const sensorTabBtn = document.querySelector('#sensor-tab-btn');
if (sensorTabBtn) {
sensorTabBtn.addEventListener('shown.bs.tab', function () {
if ($(this).data('loaded')) { return; }
$(this).data('loaded', true);
const plantId = new URLSearchParams(window.location.search).get('id');
const chartsContainer = $('#sensor-charts-container');
chartsContainer.html('<div class="col-12 text-center p-5"><div class="spinner-border text-secondary" role="status"><span class="visually-hidden">Lade...</span></div></div>');
$.ajax({
type: 'GET', url: 'ajax_handler.php', data: { action: 'get_sensor_data', plant_id: plantId }, dataType: 'json',
success: function(response) {
chartsContainer.empty();
if (response.success && response.data.labels.length > 0) {
chartsContainer.html('<div class="col-md-6" id="temp-chart-wrapper"></div><div class="col-md-6" id="humidity-chart-wrapper"></div>');
const tempWrapper = $('#temp-chart-wrapper'); const humidityWrapper = $('#humidity-chart-wrapper');
if (response.data.temperature.some(val => val !== null)) {
tempWrapper.append('<h5>Temperaturverlauf</h5>'); let tempCanvas = $('<canvas>').attr('height', '300'); tempWrapper.append($('<div>').addClass('mb-4').append(tempCanvas));
new Chart(tempCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Temperatur (°C)', data: response.data.temperature, borderColor: 'rgba(255, 99, 132, 1)', backgroundColor: 'rgba(255, 99, 132, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false } });
} else { tempWrapper.append('<p class="text-muted">Keine Temperaturdaten vorhanden.</p>'); }
if (response.data.humidity.some(val => val !== null)) {
humidityWrapper.append('<h5>Feuchtigkeitsverlauf</h5>'); let humidityCanvas = $('<canvas>').attr('height', '300'); humidityWrapper.append($('<div>').addClass('mb-4').append(humidityCanvas));
new Chart(humidityCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Feuchtigkeit (%)', data: response.data.humidity, borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false } });
} else { humidityWrapper.append('<p class="text-muted">Keine Feuchtigkeitsdaten vorhanden.</p>'); }
} else { chartsContainer.html('<div class="col-12"><p class="text-muted">Keine Sensordaten für diese Pflanze vorhanden.</p></div>'); }
},
error: function() { chartsContainer.html('<div class="col-12"><p class="text-danger">Fehler beim Laden der Sensordaten.</p></div>'); }
});
});
}
// Globale Aktivitäten Logik
const globalActivityModal = document.getElementById('globalActivityModal');
if (globalActivityModal) { globalActivityModal.addEventListener('show.bs.modal', function () { const zoneFilterSelect = $('#zone-filter-select'); if(zoneFilterSelect.children().length <= 1) { $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_all_zones' }, dataType: 'json', success: function(response) { if (response.success && response.data.length > 0) { response.data.forEach(function(zone) { zoneFilterSelect.append($('<option>', { value: zone.id, text: zone.name })); }); } } }); } const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); zoneFilterSelect.val('all').trigger('change'); }); }
$('#zone-filter-select').on('change', function() { const zoneId = $(this).val(); const selectionContainer = $('#plant-target-selection'); selectionContainer.html('<div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">Lade...</span></div>'); $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_plants_by_zone', zone_id: zoneId }, dataType: 'json', success: function(response) { selectionContainer.empty(); if (response.success && response.data.length > 0) { let plantCheckboxes = '<div class="form-check"><input class="form-check-input" type="checkbox" id="select-all-plants" checked><label class="form-check-label fw-bold" for="select-all-plants">Alle auswählen/abwählen</label></div><hr class="my-2">'; plantCheckboxes += '<div class="row" style="max-height: 200px; overflow-y: auto;">'; response.data.forEach(function(plant) { plantCheckboxes += `<div class="col-md-6"><div class="form-check"><input class="form-check-input plant-checkbox" type="checkbox" name="plant_ids[]" value="${plant.id}" id="plant_${plant.id}" checked><label class="form-check-label" for="plant_${plant.id}">${plant.strain_name} (${plant.container_name})</label></div></div>`; }); plantCheckboxes += '</div>'; selectionContainer.html(plantCheckboxes); } else { selectionContainer.html('<p class="text-muted">Keine aktiven Pflanzen in dieser Auswahl gefunden.</p>'); } } }); });
$(document).on('change', '#select-all-plants', function() { $('#global-activity-form .plant-checkbox').prop('checked', $(this).prop('checked')); });
// Universelle Logik
let itemToDelete = { id: null, type: null };
$('#deleteConfirmModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); itemToDelete.id = button.data('id'); itemToDelete.type = button.data('type'); $('#item-name-to-delete').text(button.data('name')); let typeText = 'dieses Element'; if (itemToDelete.type === 'zone') { typeText = 'die Zone'; } else if (itemToDelete.type === 'container') { typeText = 'das Pflanzgefäß'; } else if (itemToDelete.type === 'seed') { typeText = 'diesen Samen'; } else if (itemToDelete.type === 'plant') { typeText = 'diese Pflanze'; } else if (itemToDelete.type === 'plant_image') { typeText = 'dieses Bild'; } $('#item-type-to-delete').text(typeText); $('#delete-warning').toggle(itemToDelete.type === 'zone'); });
$('#confirmDeleteBtn').on('click', function() { if (!itemToDelete.id || !itemToDelete.type) return; $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'delete_' + itemToDelete.type, id: itemToDelete.id }, dataType: 'json', success: function(response) { if (response.success) { if (itemToDelete.type === 'plant') { window.location.href = 'plants.php'; } else { location.reload(); } } else { alert('Löschen fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
function handleFormSubmit(form) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: form.serialize(), dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Speichern fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); }
});

293
src/js/main.js.1346Uhr Normal file
View File

@@ -0,0 +1,293 @@
$(document).ready(function() {
// Bootstrap Initialisierungen
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
// Zonen Logik
$('#zoneModal').on('show.bs.modal', function(event) {
const button = $(event.relatedTarget);
const form = $('#zone-form');
form.trigger('reset');
if (button.data('id')) {
$('#zoneModalLabel').text('Zone bearbeiten');
form.find('[name="name"]').val(button.data('name'));
form.find('[name="id"]').val(button.data('id'));
form.find('[name="action"]').val('edit_zone');
} else {
$('#zoneModalLabel').text('Neue Zone hinzufügen');
form.find('[name="action"]').val('add_zone');
}
});
$('#zone-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
// Pflanzgefäß Logik
$('#containerModal').on('show.bs.modal', function(event) {
const button = $(event.relatedTarget);
const form = $('#container-form');
form.trigger('reset');
if (button.data('id')) {
$('#containerModalLabel').text('Pflanzgefäß bearbeiten');
form.find('[name="name"]').val(button.data('name'));
form.find('[name="id"]').val(button.data('id'));
form.find('[name="zone_id"]').val(button.data('zoneid'));
form.find('[name="action"]').val('edit_container');
} else {
$('#containerModalLabel').text('Neues Pflanzgefäß hinzufügen');
form.find('[name="action"]').val('add_container');
}
});
$('#container-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
// Samen Logik
$('#seedModal').on('show.bs.modal', function(event) {
const button = $(event.relatedTarget);
const form = $('#seed-form');
form.trigger('reset');
$('#ratio_sativa').val(50).trigger('input');
if (button.data('seed')) {
$('#seedModalLabel').text('Samen bearbeiten');
const seedData = button.data('seed');
form.find('[name="id"]').val(seedData.id);
form.find('[name="strain_name"]').val(seedData.strain_name);
form.find('[name="internal_name"]').val(seedData.internal_name);
form.find('[name="stock_count"]').val(seedData.stock_count);
form.find('[name="info_url"]').val(seedData.info_url);
form.find('[name="description"]').val(seedData.description);
form.find('[name="ratio_sativa"]').val(seedData.ratio_sativa).trigger('input');
form.find('[name="is_autoflower"]').prop('checked', seedData.is_autoflower == 1);
form.find('[name="action"]').val('edit_seed');
} else {
$('#seedModalLabel').text('Neuen Samen hinzufügen');
form.find('[name="action"]').val('add_seed');
}
});
$('#seed-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#ratio_sativa').on('input', function() { let sativa = $(this).val(); $('#sativa-value-label').text(sativa); $('#indica-value-label').text(100 - sativa); });
// Pflanzen Logik
$('#plant-form, #edit-plant-form, #activity-form, #measurement-form, #global-activity-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#image-upload-form').on('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
$.ajax({
type: 'POST', url: 'ajax_handler.php', data: formData, dataType: 'json',
contentType: false, processData: false,
success: function(response) {
if (response.success) {
location.reload();
} else {
alert('Fehler beim Upload: ' + (response.message || 'Unbekannter Fehler'));
}
},
error: function() { alert('Ein schwerwiegender Serverfehler ist aufgetreten.'); }
});
});
$('#editPlantModal').on('show.bs.modal', function(event) {
const button = $(event.relatedTarget);
const form = $('#edit-plant-form');
const plantData = button.data('plant-info');
form.find('[name="phase"]').val(plantData.phase);
form.find('[name="plant_date"]').val(plantData.plant_date);
form.find('[name="zone_id"]').val(plantData.zone_id).trigger('change', [plantData.container_id]);
});
$('#zone-select, #edit-zone-select').on('change', function(event, preselectContainerId) {
const zoneId = $(this).val();
const isEditMode = $(this).attr('id') === 'edit-zone-select';
const containerSelect = isEditMode ? $('#edit-container-select') : $('#container-select');
containerSelect.prop('disabled', true).html('<option value="">Lade Gefäße...</option>');
if (!zoneId) {
containerSelect.html('<option value="">Zuerst Zone wählen</option>');
return;
}
$.ajax({
type: 'GET', url: 'ajax_handler.php', data: { action: 'get_containers_by_zone', zone_id: zoneId }, dataType: 'json',
success: function(response) {
if (response.success) {
containerSelect.empty().append('<option value="">Pflanzgefäß wählen</option>');
if (isEditMode && preselectContainerId) {
const plantData = $('.edit-plant-btn').data('plant-info');
containerSelect.append($('<option>', { value: plantData.container_id, text: plantData.container_name }));
}
if (response.data.length > 0) {
response.data.forEach(function(container) {
if (!isEditMode || container.id != preselectContainerId) {
containerSelect.append($('<option>', { value: container.id, text: container.name }));
}
});
}
if(preselectContainerId) { containerSelect.val(preselectContainerId); }
containerSelect.prop('disabled', false);
} else { containerSelect.html('<option value="">Fehler</option>'); }
},
error: function() { containerSelect.html('<option value="">Serverfehler!</option>'); }
});
});
$('#confirmHarvestBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'harvest_plant', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#confirmFinishDryingBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'finish_drying', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#addActivityModal').on('show.bs.modal', function() { const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); });
$('#addMeasurementModal').on('show.bs.modal', function() { const today = new Date().toISOString().slice(0, 10); $(this).find('input[type="date"]').val(today); });
// Profil-Seiten Logik
$('#generate-api-key-form').on('submit', function(e) { e.preventDefault(); if (confirm('Bist du sicher? Ein neuer Key macht einen eventuell bestehenden ungültig.')) { handleFormSubmit($(this)); } });
$('#change-username-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#change-password-form').on('submit', function(e) { e.preventDefault(); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: $(this).serialize(), dataType: 'json', success: function(response) { if (response.success) { $('#changePasswordModal').modal('hide'); alert(response.message); } else { alert('Fehler: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
// Sensor-Graphen Logik
const sensorTabBtn = document.querySelector('#sensor-tab-btn');
if (sensorTabBtn) {
sensorTabBtn.addEventListener('shown.bs.tab', function () {
if ($(this).data('loaded')) { return; }
$(this).data('loaded', true);
const plantId = new URLSearchParams(window.location.search).get('id');
const chartsContainer = $('#sensor-charts-container');
chartsContainer.html('<div class="col-12 text-center p-5"><div class="spinner-border text-secondary" role="status"><span class="visually-hidden">Lade...</span></div></div>');
$.ajax({
type: 'GET', url: 'ajax_handler.php', data: { action: 'get_sensor_data', plant_id: plantId }, dataType: 'json',
success: function(response) {
chartsContainer.empty();
if (response.success && response.data.labels.length > 0) {
chartsContainer.html('<div class="col-lg-6" id="temp-chart-wrapper"></div><div class="col-lg-6" id="humidity-chart-wrapper"></div>');
const tempWrapper = $('#temp-chart-wrapper');
const humidityWrapper = $('#humidity-chart-wrapper');
if (response.data.temperature.some(val => val !== null)) {
tempWrapper.append('<h5>Temperaturverlauf</h5>');
let tempCanvas = $('<canvas>').attr('height', '300');
tempWrapper.append($('<div>').addClass('mb-4').append(tempCanvas));
new Chart(tempCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Temperatur (°C)', data: response.data.temperature, borderColor: 'rgba(255, 99, 132, 1)', backgroundColor: 'rgba(255, 99, 132, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false } });
} else {
tempWrapper.append('<p class="text-muted">Keine Temperaturdaten vorhanden.</p>');
}
if (response.data.humidity.some(val => val !== null)) {
humidityWrapper.append('<h5>Feuchtigkeitsverlauf</h5>');
let humidityCanvas = $('<canvas>').attr('height', '300');
humidityWrapper.append($('<div>').addClass('mb-4').append(humidityCanvas));
new Chart(humidityCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Feuchtigkeit (%)', data: response.data.humidity, borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false } });
} else {
humidityWrapper.append('<p class="text-muted">Keine Feuchtigkeitsdaten vorhanden.</p>');
}
} else {
chartsContainer.html('<div class="col-12"><p class="text-muted">Keine Sensordaten für diese Pflanze vorhanden.</p></div>');
}
},
error: function() {
chartsContainer.html('<div class="col-12"><p class="text-danger">Fehler beim Laden der Sensordaten.</p></div>');
}
});
});
}
// Globale Aktivitäten Logik
const globalActivityModal = document.getElementById('globalActivityModal');
if (globalActivityModal) {
globalActivityModal.addEventListener('show.bs.modal', function () {
const zoneFilterSelect = $('#zone-filter-select');
if(zoneFilterSelect.children().length <= 1) {
$.ajax({
type: 'GET', url: 'ajax_handler.php', data: { action: 'get_all_zones' }, dataType: 'json',
success: function(response) {
if (response.success && response.data.length > 0) {
response.data.forEach(function(zone) {
zoneFilterSelect.append($('<option>', { value: zone.id, text: zone.name }));
});
}
}
});
}
const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16);
$(this).find('input[type="datetime-local"]').val(localDateTime);
zoneFilterSelect.val('all').trigger('change');
});
}
$('#zone-filter-select').on('change', function() {
const zoneId = $(this).val();
const selectionContainer = $('#plant-target-selection');
selectionContainer.html('<div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">Lade...</span></div>');
$.ajax({
type: 'GET', url: 'ajax_handler.php', data: { action: 'get_plants_by_zone', zone_id: zoneId }, dataType: 'json',
success: function(response) {
selectionContainer.empty();
if (response.success && response.data.length > 0) {
let plantCheckboxes = '<div class="form-check"><input class="form-check-input" type="checkbox" id="select-all-plants" checked><label class="form-check-label fw-bold" for="select-all-plants">Alle auswählen/abwählen</label></div><hr class="my-2">';
plantCheckboxes += '<div class="row" style="max-height: 200px; overflow-y: auto;">';
response.data.forEach(function(plant) {
plantCheckboxes += `<div class="col-md-6"><div class="form-check"><input class="form-check-input plant-checkbox" type="checkbox" name="plant_ids[]" value="${plant.id}" id="plant_${plant.id}" checked><label class="form-check-label" for="plant_${plant.id}">${plant.strain_name} (${plant.container_name})</label></div></div>`;
});
plantCheckboxes += '</div>';
selectionContainer.html(plantCheckboxes);
} else {
selectionContainer.html('<p class="text-muted">Keine aktiven Pflanzen in dieser Auswahl gefunden.</p>');
}
}
});
});
$(document).on('change', '#select-all-plants', function() {
$('#global-activity-form .plant-checkbox').prop('checked', $(this).prop('checked'));
});
// Universelle Logik
let itemToDelete = { id: null, type: null };
$('#deleteConfirmModal').on('show.bs.modal', function(event) {
const button = $(event.relatedTarget);
itemToDelete.id = button.data('id');
itemToDelete.type = button.data('type');
$('#item-name-to-delete').text(button.data('name'));
let typeText = 'dieses Element';
if (itemToDelete.type === 'zone') { typeText = 'die Zone'; }
else if (itemToDelete.type === 'container') { typeText = 'das Pflanzgefäß'; }
else if (itemToDelete.type === 'seed') { typeText = 'diesen Samen'; }
else if (itemToDelete.type === 'plant') { typeText = 'diese Pflanze'; }
else if (itemToDelete.type === 'plant_image') { typeText = 'dieses Bild'; }
$('#item-type-to-delete').text(typeText);
$('#delete-warning').toggle(itemToDelete.type === 'zone');
});
$('#confirmDeleteBtn').on('click', function() {
if (!itemToDelete.id || !itemToDelete.type) return;
$.ajax({
type: 'POST', url: 'ajax_handler.php',
data: { action: 'delete_' + itemToDelete.type, id: itemToDelete.id },
dataType: 'json',
success: function(response) {
if (response.success) {
if (itemToDelete.type === 'plant') {
window.location.href = 'plants.php';
} else {
location.reload();
}
} else {
alert('Löschen fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler'));
}
},
error: () => alert('Serverfehler!')
});
});
function handleFormSubmit(form) {
$.ajax({
type: 'POST',
url: 'ajax_handler.php',
data: form.serialize(),
dataType: 'json',
success: function(response) {
if (response.success) {
location.reload();
} else {
alert('Speichern fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler'));
}
},
error: () => alert('Serverfehler!')
});
}
});

58
src/js/main.js.1357Uhr Normal file
View File

@@ -0,0 +1,58 @@
$(document).ready(function() {
// Bootstrap Initialisierungen
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl));
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));
// NEU: DataTables für die Samen-Tabelle initialisieren
if ($('#seeds-table').length) {
$('#seeds-table').DataTable({
"dom": "t", // 't' bedeutet, dass NUR die Tabelle gerendert wird, ohne zusätzliche Controls
"paging": false, // Keine Paginierung
"info": false, // Keine "Zeige x von y Einträgen"-Info
"language": {
"zeroRecords": "Keine passenden Samen gefunden",
},
"columnDefs": [
{ "orderable": false, "targets": 'no-sort' }
]
});
}
// Zonen Logik
$('#zoneModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#zone-form'); form.trigger('reset'); if (button.data('id')) { $('#zoneModalLabel').text('Zone bearbeiten'); form.find('[name="name"]').val(button.data('name')); form.find('[name="id"]').val(button.data('id')); form.find('[name="action"]').val('edit_zone'); } else { $('#zoneModalLabel').text('Neue Zone hinzufügen'); form.find('[name="action"]').val('add_zone'); } });
$('#zone-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
// Pflanzgefäß Logik
$('#containerModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#container-form'); form.trigger('reset'); if (button.data('id')) { $('#containerModalLabel').text('Pflanzgefäß bearbeiten'); form.find('[name="name"]').val(button.data('name')); form.find('[name="id"]').val(button.data('id')); form.find('[name="zone_id"]').val(button.data('zoneid')); form.find('[name="action"]').val('edit_container'); } else { $('#containerModalLabel').text('Neues Pflanzgefäß hinzufügen'); form.find('[name="action"]').val('add_container'); } });
$('#container-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
// Samen Logik
$('#seedModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#seed-form'); form.trigger('reset'); $('#ratio_sativa').val(50).trigger('input'); if (button.data('seed')) { $('#seedModalLabel').text('Samen bearbeiten'); const seedData = button.data('seed'); form.find('[name="id"]').val(seedData.id); form.find('[name="strain_name"]').val(seedData.strain_name); form.find('[name="internal_name"]').val(seedData.internal_name); form.find('[name="stock_count"]').val(seedData.stock_count); form.find('[name="info_url"]').val(seedData.info_url); form.find('[name="description"]').val(seedData.description); form.find('[name="ratio_sativa"]').val(seedData.ratio_sativa).trigger('input'); form.find('[name="is_autoflower"]').prop('checked', seedData.is_autoflower == 1); form.find('[name="action"]').val('edit_seed'); } else { $('#seedModalLabel').text('Neuen Samen hinzufügen'); form.find('[name="action"]').val('add_seed'); } });
$('#seed-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#ratio_sativa').on('input', function() { let sativa = $(this).val(); $('#sativa-value-label').text(sativa); $('#indica-value-label').text(100 - sativa); });
// Pflanzen Logik
$('#plant-form, #edit-plant-form, #activity-form, #measurement-form, #global-activity-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#image-upload-form').on('submit', function(e) { e.preventDefault(); const formData = new FormData(this); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: formData, dataType: 'json', contentType: false, processData: false, success: function(response) { if (response.success) { location.reload(); } else { alert('Fehler beim Upload: ' + (response.message || 'Unbekannter Fehler')); } }, error: function() { alert('Ein schwerwiegender Serverfehler ist aufgetreten.'); } }); });
$('#editPlantModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); const form = $('#edit-plant-form'); const plantData = button.data('plant-info'); form.find('[name="phase"]').val(plantData.phase); form.find('[name="plant_date"]').val(plantData.plant_date); form.find('[name="zone_id"]').val(plantData.zone_id).trigger('change', [plantData.container_id]); });
$('#zone-select, #edit-zone-select').on('change', function(event, preselectContainerId) { const zoneId = $(this).val(); const isEditMode = $(this).attr('id') === 'edit-zone-select'; const containerSelect = isEditMode ? $('#edit-container-select') : $('#container-select'); containerSelect.prop('disabled', true).html('<option value="">Lade Gefäße...</option>'); if (!zoneId) { containerSelect.html('<option value="">Zuerst Zone wählen</option>'); return; } $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_containers_by_zone', zone_id: zoneId }, dataType: 'json', success: function(response) { if (response.success) { containerSelect.empty().append('<option value="">Pflanzgefäß wählen</option>'); if (isEditMode && preselectContainerId) { const plantData = $('.edit-plant-btn').data('plant-info'); containerSelect.append($('<option>', { value: plantData.container_id, text: plantData.container_name })); } if (response.data.length > 0) { response.data.forEach(function(container) { if (!isEditMode || container.id != preselectContainerId) { containerSelect.append($('<option>', { value: container.id, text: container.name })); } }); } if(preselectContainerId) { containerSelect.val(preselectContainerId); } containerSelect.prop('disabled', false); } else { containerSelect.html('<option value="">Fehler</option>'); } }, error: function() { containerSelect.html('<option value="">Serverfehler!</option>'); } }); });
$('#confirmHarvestBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'harvest_plant', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#confirmFinishDryingBtn').on('click', function() { const plantId = $('#edit-plant-form [name="plant_id"]').val(); if (plantId) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'finish_drying', plant_id: plantId }, dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Aktion fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); } });
$('#addActivityModal').on('show.bs.modal', function() { const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); });
$('#addMeasurementModal').on('show.bs.modal', function() { const today = new Date().toISOString().slice(0, 10); $(this).find('input[type="date"]').val(today); });
$('#generate-api-key-form').on('submit', function(e) { e.preventDefault(); if (confirm('Bist du sicher? Ein neuer Key macht einen eventuell bestehenden ungültig.')) { handleFormSubmit($(this)); } });
$('#change-username-form').on('submit', function(e) { e.preventDefault(); handleFormSubmit($(this)); });
$('#change-password-form').on('submit', function(e) { e.preventDefault(); $.ajax({ type: 'POST', url: 'ajax_handler.php', data: $(this).serialize(), dataType: 'json', success: function(response) { if (response.success) { $('#changePasswordModal').modal('hide'); alert(response.message); } else { alert('Fehler: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
const sensorTabBtn = document.querySelector('#sensor-tab-btn'); if (sensorTabBtn) { sensorTabBtn.addEventListener('shown.bs.tab', function () { if ($(this).data('loaded')) { return; } $(this).data('loaded', true); const plantId = new URLSearchParams(window.location.search).get('id'); const chartsContainer = $('#sensor-charts-container'); chartsContainer.html('<div class="col-12 text-center p-5"><div class="spinner-border text-secondary" role="status"><span class="visually-hidden">Lade...</span></div></div>'); $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_sensor_data', plant_id: plantId }, dataType: 'json', success: function(response) { chartsContainer.empty(); if (response.success && response.data.labels.length > 0) { chartsContainer.html('<div class="col-lg-6" id="temp-chart-wrapper"></div><div class="col-lg-6" id="humidity-chart-wrapper"></div>'); const tempWrapper = $('#temp-chart-wrapper'); const humidityWrapper = $('#humidity-chart-wrapper'); if (response.data.temperature.some(val => val !== null)) { tempWrapper.append('<h5>Temperaturverlauf</h5>'); let tempCanvas = $('<canvas>').attr('height', '300'); tempWrapper.append($('<div>').addClass('mb-4').append(tempCanvas)); new Chart(tempCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Temperatur (°C)', data: response.data.temperature, borderColor: 'rgba(255, 99, 132, 1)', backgroundColor: 'rgba(255, 99, 132, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false } }); } else { tempWrapper.append('<p class="text-muted">Keine Temperaturdaten vorhanden.</p>'); } if (response.data.humidity.some(val => val !== null)) { humidityWrapper.append('<h5>Feuchtigkeitsverlauf</h5>'); let humidityCanvas = $('<canvas>').attr('height', '300'); humidityWrapper.append($('<div>').addClass('mb-4').append(humidityCanvas)); new Chart(humidityCanvas, { type: 'line', data: { labels: response.data.labels, datasets: [{ label: 'Feuchtigkeit (%)', data: response.data.humidity, borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', fill: true, tension: 0.2, spanGaps: true }] }, options: { maintainAspectRatio: false } }); } else { humidityWrapper.append('<p class="text-muted">Keine Feuchtigkeitsdaten vorhanden.</p>'); } } else { chartsContainer.html('<div class="col-12"><p class="text-muted">Keine Sensordaten für diese Pflanze vorhanden.</p></div>'); } }, error: function() { chartsContainer.html('<div class="col-12"><p class="text-danger">Fehler beim Laden der Sensordaten.</p></div>'); } }); }); }
const globalActivityModal = document.getElementById('globalActivityModal'); if (globalActivityModal) { globalActivityModal.addEventListener('show.bs.modal', function () { const zoneFilterSelect = $('#zone-filter-select'); if(zoneFilterSelect.children().length <= 1) { $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_all_zones' }, dataType: 'json', success: function(response) { if (response.success && response.data.length > 0) { response.data.forEach(function(zone) { zoneFilterSelect.append($('<option>', { value: zone.id, text: zone.name })); }); } } }); } const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); const localDateTime = now.toISOString().slice(0,16); $(this).find('input[type="datetime-local"]').val(localDateTime); zoneFilterSelect.val('all').trigger('change'); }); }
$('#zone-filter-select').on('change', function() { const zoneId = $(this).val(); const selectionContainer = $('#plant-target-selection'); selectionContainer.html('<div class="spinner-border spinner-border-sm" role="status"><span class="visually-hidden">Lade...</span></div>'); $.ajax({ type: 'GET', url: 'ajax_handler.php', data: { action: 'get_plants_by_zone', zone_id: zoneId }, dataType: 'json', success: function(response) { selectionContainer.empty(); if (response.success && response.data.length > 0) { let plantCheckboxes = '<div class="form-check"><input class="form-check-input" type="checkbox" id="select-all-plants" checked><label class="form-check-label fw-bold" for="select-all-plants">Alle auswählen/abwählen</label></div><hr class="my-2">'; plantCheckboxes += '<div class="row" style="max-height: 200px; overflow-y: auto;">'; response.data.forEach(function(plant) { plantCheckboxes += `<div class="col-md-6"><div class="form-check"><input class="form-check-input plant-checkbox" type="checkbox" name="plant_ids[]" value="${plant.id}" id="plant_${plant.id}" checked><label class="form-check-label" for="plant_${plant.id}">${plant.strain_name} (${plant.container_name})</label></div></div>`; }); plantCheckboxes += '</div>'; selectionContainer.html(plantCheckboxes); } else { selectionContainer.html('<p class="text-muted">Keine aktiven Pflanzen in dieser Auswahl gefunden.</p>'); } } }); });
$(document).on('change', '#select-all-plants', function() { $('#global-activity-form .plant-checkbox').prop('checked', $(this).prop('checked')); });
// Universelle Logik
let itemToDelete = { id: null, type: null };
$('#deleteConfirmModal').on('show.bs.modal', function(event) { const button = $(event.relatedTarget); itemToDelete.id = button.data('id'); itemToDelete.type = button.data('type'); $('#item-name-to-delete').text(button.data('name')); let typeText = 'dieses Element'; if (itemToDelete.type === 'zone') { typeText = 'die Zone'; } else if (itemToDelete.type === 'container') { typeText = 'das Pflanzgefäß'; } else if (itemToDelete.type === 'seed') { typeText = 'diesen Samen'; } else if (itemToDelete.type === 'plant') { typeText = 'diese Pflanze'; } else if (itemToDelete.type === 'plant_image') { typeText = 'dieses Bild'; } $('#item-type-to-delete').text(typeText); $('#delete-warning').toggle(itemToDelete.type === 'zone'); });
$('#confirmDeleteBtn').on('click', function() { if (!itemToDelete.id || !itemToDelete.type) return; $.ajax({ type: 'POST', url: 'ajax_handler.php', data: { action: 'delete_' + itemToDelete.type, id: itemToDelete.id }, dataType: 'json', success: function(response) { if (response.success) { if (itemToDelete.type === 'plant') { window.location.href = 'plants.php'; } else { location.reload(); } } else { alert('Löschen fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); });
function handleFormSubmit(form) { $.ajax({ type: 'POST', url: 'ajax_handler.php', data: form.serialize(), dataType: 'json', success: function(response) { if (response.success) { location.reload(); } else { alert('Speichern fehlgeschlagen: ' + (response.message || 'Unbekannter Fehler')); } }, error: () => alert('Serverfehler!') }); }
});