Refactor: Moved app source code back to 'src/' directory for cleaner Docker build context
This commit is contained in:
1 parent
1d9424046e
commit
72d56c31f7
224 files changed
+172
-9
No files matched your search
+1
-1
@@ -12,7 +12,7 @@ RUN apt-get update && apt-get install -y \
|
||||
RUN a2enmod rewrite
|
||||
|
||||
# Copy application source
|
||||
COPY . /var/www/html/
|
||||
COPY src/ /var/www/html/
|
||||
|
||||
# Set permissions for upload directory
|
||||
RUN mkdir -p /var/www/html/uploads/images && \
|
||||
|
||||
Executable → Regular
+20
@@ -209,6 +209,10 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$conn->close();
|
||||
?>
|
||||
|
||||
<!-- Tom Select CSS/JS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.bootstrap5.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2 class="h4 mb-0"><i class="fas fa-plus-circle me-2"></i>Neuen Artikel erstellen</h2>
|
||||
@@ -348,6 +352,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
quantityOwnedInput.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize Tom Select for searchable dropdowns
|
||||
const tsOptions = {
|
||||
create: false,
|
||||
sortField: { field: "text", direction: "asc" },
|
||||
onChange: function(value) {
|
||||
// Force trigger change event for the "New" option toggle logic
|
||||
const event = new Event('change');
|
||||
this.input.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
if(document.getElementById('manufacturer_id')) new TomSelect('#manufacturer_id', tsOptions);
|
||||
if(document.getElementById('category_id')) new TomSelect('#category_id', tsOptions);
|
||||
if(document.getElementById('parent_article_id')) new TomSelect('#parent_article_id', tsOptions);
|
||||
if(document.getElementById('storage_location_id')) new TomSelect('#storage_location_id', tsOptions);
|
||||
});
|
||||
</script>
|
||||
<?php require_once 'footer.php'; ?>
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
@@ -460,9 +460,16 @@ body::before {
|
||||
.section-card {
|
||||
border: 1px solid rgba(0,0,0,0.08);
|
||||
border-radius: var(--border-radius-md);
|
||||
overflow: hidden; /* Ensure header radius respects container */
|
||||
overflow: visible; /* Allow dropdowns to overflow */
|
||||
background-color: var(--glass-bg);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
position: relative;
|
||||
transition: z-index 0.1s step-start; /* Instant z-index change */
|
||||
}
|
||||
|
||||
/* Lift card when interacting to ensure dropdowns appear over subsequent cards */
|
||||
.section-card:hover, .section-card:focus-within {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.section-card .card-header {
|
||||
@@ -470,10 +477,13 @@ body::before {
|
||||
color: var(--color-primary-dark);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.05);
|
||||
font-weight: 700;
|
||||
border-top-left-radius: var(--border-radius-md);
|
||||
border-top-right-radius: var(--border-radius-md);
|
||||
}
|
||||
|
||||
.section-card .card-body {
|
||||
background-color: transparent;
|
||||
overflow: visible; /* Allow dropdowns to overflow */
|
||||
}
|
||||
|
||||
.paste-area {
|
||||
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
@@ -9,7 +9,7 @@ $dbname = getenv('DB_NAME');
|
||||
|
||||
// 2. Fallback: config.ini (Lokale Entwicklung)
|
||||
if (!$servername || !$username || !$dbname) {
|
||||
$config_path = __DIR__ . '/config.ini';
|
||||
$config_path = __DIR__ . '/../config.ini';
|
||||
if (file_exists($config_path)) {
|
||||
$config = parse_ini_file($config_path);
|
||||
if ($config) {
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
+19
@@ -260,6 +260,10 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" && $can_edit) {
|
||||
$conn->close();
|
||||
?>
|
||||
|
||||
<!-- Tom Select CSS/JS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.bootstrap5.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h2 class="h4 mb-0"><i class="fas fa-edit me-2"></i>Artikel bearbeiten</h2>
|
||||
@@ -422,6 +426,21 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
quantityOwnedInput.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize Tom Select
|
||||
const tsOptions = {
|
||||
create: false,
|
||||
sortField: { field: "text", direction: "asc" },
|
||||
onChange: function(value) {
|
||||
const event = new Event('change');
|
||||
this.input.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
if(document.getElementById('manufacturer_id')) new TomSelect('#manufacturer_id', tsOptions);
|
||||
if(document.getElementById('category_id')) new TomSelect('#category_id', tsOptions);
|
||||
if(document.getElementById('parent_article_id')) new TomSelect('#parent_article_id', tsOptions);
|
||||
if(document.getElementById('storage_location_id')) new TomSelect('#storage_location_id', tsOptions);
|
||||
});
|
||||
</script>
|
||||
<?php require_once 'footer.php'; ?>
|
||||
Executable → Regular
+82
-2
@@ -90,10 +90,41 @@ if ($backpack_id > 0) {
|
||||
}
|
||||
}
|
||||
|
||||
// Load Manufacturers
|
||||
$stmt_man_load = $conn->prepare("SELECT id, name FROM manufacturers WHERE user_id = ? ORDER BY name ASC");
|
||||
$stmt_man_load->bind_param("i", $user_id);
|
||||
$stmt_man_load->execute();
|
||||
$manufacturers = $stmt_man_load->get_result()->fetch_all(MYSQLI_ASSOC);
|
||||
$stmt_man_load->close();
|
||||
|
||||
// Handle Form Submission BEFORE loading header
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$name = trim($_POST['name']);
|
||||
$manufacturer = trim($_POST['manufacturer']);
|
||||
|
||||
// Manufacturer Logic
|
||||
$manufacturer = '';
|
||||
if (isset($_POST['manufacturer_select'])) {
|
||||
if ($_POST['manufacturer_select'] === 'new') {
|
||||
$new_man = trim($_POST['new_manufacturer_name']);
|
||||
if (!empty($new_man)) {
|
||||
// Optional: Save to manufacturers table for future use
|
||||
$stmt_new_man = $conn->prepare("INSERT INTO manufacturers (name, user_id) VALUES (?, ?)");
|
||||
$stmt_new_man->bind_param("si", $new_man, $user_id);
|
||||
$stmt_new_man->execute();
|
||||
$manufacturer = $new_man;
|
||||
}
|
||||
} else {
|
||||
// Look up name from ID
|
||||
$man_id = intval($_POST['manufacturer_select']);
|
||||
foreach ($manufacturers as $m) {
|
||||
if ($m['id'] == $man_id) {
|
||||
$manufacturer = $m['name'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$model = trim($_POST['model']);
|
||||
$weight = intval($_POST['weight_grams']);
|
||||
$volume = intval($_POST['volume_liters']);
|
||||
@@ -212,9 +243,16 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Handle Form Submission BEFORE loading header
|
||||
// ... (existing logic) ...
|
||||
|
||||
require_once 'header.php';
|
||||
?>
|
||||
|
||||
<!-- Tom Select CSS/JS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/css/tom-select.bootstrap5.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.2.2/dist/js/tom-select.complete.min.js"></script>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
@@ -237,7 +275,20 @@ require_once 'header.php';
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Hersteller</label>
|
||||
<input type="text" name="manufacturer" class="form-control" value="<?php echo htmlspecialchars($backpack['manufacturer'] ?? ''); ?>">
|
||||
<select class="form-select" id="manufacturer_select" name="manufacturer_select">
|
||||
<option value="">-- Auswählen --</option>
|
||||
<option value="new">-- Neuen anlegen --</option>
|
||||
<?php
|
||||
$current_man = $backpack['manufacturer'] ?? '';
|
||||
foreach ($manufacturers as $man):
|
||||
$selected = ($man['name'] === $current_man) ? 'selected' : '';
|
||||
?>
|
||||
<option value="<?php echo $man['id']; ?>" <?php echo $selected; ?>><?php echo htmlspecialchars($man['name']); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div id="new_manufacturer_container" class="mt-2" style="display:none;">
|
||||
<input type="text" name="new_manufacturer_name" class="form-control form-control-sm" placeholder="Name des neuen Herstellers">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">Modell</label>
|
||||
@@ -324,6 +375,21 @@ require_once 'header.php';
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const manSelect = document.getElementById('manufacturer_select');
|
||||
const manContainer = document.getElementById('new_manufacturer_container');
|
||||
|
||||
if (manSelect) {
|
||||
manSelect.addEventListener('change', function() {
|
||||
if (this.value === 'new') {
|
||||
manContainer.style.display = 'block';
|
||||
manContainer.querySelector('input').required = true;
|
||||
} else {
|
||||
manContainer.style.display = 'none';
|
||||
manContainer.querySelector('input').required = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const container = document.getElementById('compartments-container');
|
||||
|
||||
// Sortable for Compartments
|
||||
@@ -367,6 +433,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
|
||||
if (pasteArea) {
|
||||
// ... existing paste logic ...
|
||||
pasteArea.addEventListener('paste', function(e) {
|
||||
e.preventDefault();
|
||||
const items = (e.clipboardData || window.clipboardData).items;
|
||||
@@ -388,6 +455,19 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
pasteArea.setAttribute('tabindex', '0');
|
||||
pasteArea.addEventListener('click', () => pasteArea.focus());
|
||||
}
|
||||
|
||||
// Tom Select Init
|
||||
const manSelect = document.getElementById('manufacturer_select');
|
||||
if(manSelect) {
|
||||
new TomSelect(manSelect, {
|
||||
create: false,
|
||||
sortField: { field: "text", direction: "asc" },
|
||||
onChange: function(value) {
|
||||
const event = new Event('change');
|
||||
manSelect.dispatchEvent(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
Executable → Regular
+1
-2
@@ -365,5 +365,4 @@ function selectBackpack(userId, bpId) {
|
||||
.bp-select-card:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); border-color: var(--color-primary); }
|
||||
</style>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
</replace>
|
||||
<?php require_once 'footer.php'; ?>
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
+37
-2
@@ -51,7 +51,7 @@ if ($current_user_household_id) {
|
||||
$placeholders = implode(',', array_fill(0, count($household_member_ids), '?'));
|
||||
$types = str_repeat('i', count($household_member_ids));
|
||||
|
||||
$sql_all_articles = "SELECT a.id, a.name, a.weight_grams, a.quantity_owned, a.product_designation, a.consumable, a.parent_article_id, c.name as category_name, m.name as manufacturer_name
|
||||
$sql_all_articles = "SELECT a.id, a.name, a.weight_grams, a.quantity_owned, a.product_designation, a.consumable, a.parent_article_id, a.image_url, c.name as category_name, m.name as manufacturer_name
|
||||
FROM articles a
|
||||
LEFT JOIN categories c ON a.category_id = c.id
|
||||
LEFT JOIN manufacturers m ON a.manufacturer_id = m.id
|
||||
@@ -150,6 +150,7 @@ $conn->close();
|
||||
</div>
|
||||
|
||||
<div id="save-feedback" class="save-feedback">Änderungen gespeichert!</div>
|
||||
<div id="image-preview-tooltip" class="image-preview-tooltip"></div>
|
||||
|
||||
<style>
|
||||
/* Nested Sortable Specific Styles */
|
||||
@@ -216,6 +217,32 @@ $conn->close();
|
||||
let childrenModal = null;
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Tooltip Logic
|
||||
const tooltip = document.getElementById('image-preview-tooltip');
|
||||
if (tooltip) {
|
||||
// Delegation for dynamically added items
|
||||
document.body.addEventListener('mouseover', e => {
|
||||
if (e.target.classList.contains('article-image-trigger')) {
|
||||
const url = e.target.getAttribute('data-preview-url');
|
||||
if (url && !url.endsWith('keinbild.png')) {
|
||||
tooltip.style.backgroundImage = `url('${url}')`;
|
||||
tooltip.style.display = 'block';
|
||||
}
|
||||
}
|
||||
});
|
||||
document.body.addEventListener('mousemove', e => {
|
||||
if (tooltip.style.display === 'block') {
|
||||
tooltip.style.left = e.pageX + 15 + 'px';
|
||||
tooltip.style.top = e.pageY + 15 + 'px';
|
||||
}
|
||||
});
|
||||
document.body.addEventListener('mouseout', e => {
|
||||
if (e.target.classList.contains('article-image-trigger')) {
|
||||
tooltip.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
['input', 'change'].forEach(evt => {
|
||||
document.getElementById('filter-text').addEventListener(evt, renderAvailableItems);
|
||||
document.getElementById('filter-category').addEventListener(evt, renderAvailableItems);
|
||||
@@ -246,7 +273,15 @@ $conn->close();
|
||||
const matchesManufacturer = !filterManufacturer || article.manufacturer_name === filterManufacturer;
|
||||
if (matchesText && matchesCategory && matchesManufacturer) {
|
||||
const details = [article.manufacturer_name, article.product_designation].filter(Boolean).join(' - ');
|
||||
html += `<div class="available-item-card" data-article-id="${article.id}">${article.name} <small class="text-muted">(${details || '---'} | ${article.weight_grams}g)</small></div>`;
|
||||
const imgUrl = article.image_url ? article.image_url : 'keinbild.png';
|
||||
html += `
|
||||
<div class="available-item-card d-flex align-items-center" data-article-id="${article.id}">
|
||||
<img src="${imgUrl}" class="article-image-trigger me-2 rounded" style="width: 30px; height: 30px; object-fit: cover; cursor: pointer;" data-preview-url="${imgUrl}">
|
||||
<div class="flex-grow-1">
|
||||
${article.name}
|
||||
<small class="text-muted d-block" style="line-height: 1;">(${details || '---'} | ${article.weight_grams}g)</small>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
});
|
||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 224 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user