Dodatkowe wytyczne w opisie produktu

This commit is contained in:
Flatlogic Bot 2026-01-09 12:30:38 +00:00
parent 834f5e016d
commit b29ec1b4b5
7 changed files with 504 additions and 6 deletions

View File

@ -50,6 +50,29 @@ if (isset($_GET['delete_document']) && isset($_GET['id'])) {
exit; exit;
} }
// Handle material deletion
if (isset($_GET['delete_material']) && isset($_GET['id'])) {
$material_id_to_delete = $_GET['delete_material'];
$product_id_for_redirect = $_GET['id'];
$material_stmt = $pdo->prepare("SELECT path FROM product_materials WHERE id = ? AND product_id = ?");
$material_stmt->execute([$material_id_to_delete, $product_id_for_redirect]);
$material_to_delete = $material_stmt->fetch(PDO::FETCH_ASSOC);
if ($material_to_delete && !empty($material_to_delete['path'])) {
$file_path = __DIR__ . '/../uploads/products/' . $material_to_delete['path'];
if (file_exists($file_path)) {
unlink($file_path);
}
}
$delete_stmt = $pdo->prepare("DELETE FROM product_materials WHERE id = ?");
$delete_stmt->execute([$material_id_to_delete]);
header('Location: edit_product.php?id=' . $product_id_for_redirect);
exit;
}
$product = [ $product = [
'id' => null, 'id' => null,
@ -104,6 +127,11 @@ if (isset($_GET['id'])) {
$docs_stmt->execute([$product_id]); $docs_stmt->execute([$product_id]);
$product_documents = $docs_stmt->fetchAll(PDO::FETCH_ASSOC); $product_documents = $docs_stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch product materials
$materials_stmt = $pdo->prepare("SELECT * FROM product_materials WHERE product_id = ?");
$materials_stmt->execute([$product_id]);
$product_materials = $materials_stmt->fetchAll(PDO::FETCH_ASSOC);
} }
@ -244,6 +272,60 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} }
} }
// Handle material uploads
if (isset($_POST['material_type']) && !empty($_POST['material_type'])) {
$material_type = $_POST['material_type'];
$material_title = $_POST['material_title'] ?? '';
$material_errors = [];
if (empty($material_title)) {
$material_errors[] = 'Tytuł materiału jest wymagany.';
}
if ($material_type === 'youtube') {
$material_url = $_POST['material_url'] ?? '';
if (empty($material_url) || !filter_var($material_url, FILTER_VALIDATE_URL)) {
$material_errors[] = 'Nieprawidłowy adres URL YouTube.';
} else {
$material_stmt = $pdo->prepare("INSERT INTO product_materials (product_id, material_type, title, url) VALUES (?, ?, ?, ?)");
$material_stmt->execute([$product_id, $material_type, $material_title, $material_url]);
}
} else { // pdf or image
if (isset($_FILES['material_file']) && $_FILES['material_file']['error'] === UPLOAD_ERR_OK) {
$allowed_types = ['application/pdf', 'image/jpeg', 'image/png'];
$file_type = mime_content_type($_FILES['material_file']['tmp_name']);
if (in_array($file_type, $allowed_types)) {
$upload_dir = __DIR__ . '/../uploads/products/' . $product_id . '/materials/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
$file_ext = pathinfo($_FILES['material_file']['name'], PATHINFO_EXTENSION);
$file_name = uniqid('mat_' . $product_id . '_', true) . '.' . $file_ext;
$destination = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['material_file']['tmp_name'], $destination)) {
$material_stmt = $pdo->prepare("INSERT INTO product_materials (product_id, material_type, title, path) VALUES (?, ?, ?, ?)");
$material_stmt->execute([$product_id, $material_type, $material_title, $product_id . '/materials/' . $file_name]);
} else {
$material_errors[] = "Nie udało się przenieść pliku materiału.";
}
} else {
$material_errors[] = "Niedozwolony typ pliku materiału.";
}
} else {
$material_errors[] = "Błąd podczas przesyłania pliku materiału.";
}
}
if (!empty($material_errors)) {
$errors = array_merge($errors, $material_errors);
if ($pdo->inTransaction()) $pdo->rollBack();
goto end_of_post_handling;
}
}
$clear_stmt = $pdo->prepare("DELETE FROM product_attributes WHERE product_id = ?"); $clear_stmt = $pdo->prepare("DELETE FROM product_attributes WHERE product_id = ?");
@ -418,12 +500,60 @@ $page_title = $product['id'] ? 'Edytuj produkt' : 'Dodaj produkt';
endif; ?> endif; ?>
</div> </div>
<div class="card card-body mb-4">
<h5>Dodatkowe materiały</h5>
<div class="mb-3">
<label for="material_type" class="form-label">Typ materiału</label>
<select name="material_type" id="material_type" class="form-select">
<option value="">-- Wybierz typ --</option>
<option value="pdf">PDF</option>
<option value="image">Zdjęcie</option>
<option value="youtube">YouTube</option>
</select>
</div>
<div class="mb-3">
<label for="material_title" class="form-label">Tytuł</label>
<input type="text" class="form-control" id="material_title" name="material_title">
</div>
<div class="mb-3" id="material_file_div">
<label for="material_file" class="form-label">Plik</label>
<input type="file" class="form-control" id="material_file" name="material_file">
</div>
<div class="mb-3" id="material_url_div" style="display: none;">
<label for="material_url" class="form-label">URL</label>
<input type="url" class="form-control" id="material_url" name="material_url">
</div>
<?php if (!empty($product_materials)):
echo '<ul class="list-group">';
foreach($product_materials as $material) {
echo '<li class="list-group-item d-flex justify-content-between align-items-center">';
echo htmlspecialchars($material['title']) . ' (' . htmlspecialchars($material['material_type']) . ')';
echo '<a href="edit_product.php?id=' . htmlspecialchars($product['id']) . '&delete_material=' . htmlspecialchars($material['id']) . '" class="btn btn-danger btn-sm" onclick="return confirm(\'Czy na pewno chcesz usunąć ten materiał?\');">Usuń</a>';
echo '</li>';
}
echo '</ul>';
endif; ?>
</div>
<div class="mt-4"> <div class="mt-4">
<button type="submit" class="btn btn-primary">Zapisz</button> <button type="submit" class="btn btn-primary">Zapisz</button>
<a href="products.php" class="btn btn-secondary">Anuluj</a> <a href="products.php" class="btn btn-secondary">Anuluj</a>
</div> </div>
</form> </form>
</main> </main>
<script>
document.getElementById('material_type').addEventListener('change', function() {
var fileDiv = document.getElementById('material_file_div');
var urlDiv = document.getElementById('material_url_div');
if (this.value === 'youtube') {
fileDiv.style.display = 'none';
urlDiv.style.display = 'block';
} else {
fileDiv.style.display = 'block';
urlDiv.style.display = 'none';
}
});
</script>
<?php require_once __DIR__ . '/../includes/footer.php'; ?> <?php require_once __DIR__ . '/../includes/footer.php'; ?>
</body> </body>
</html> </html>

View File

@ -33,8 +33,6 @@ foreach ($all_files as $file) {
echo "Applying migration: $filename\n"; echo "Applying migration: $filename\n";
try { try {
$pdo->beginTransaction();
$sql = file_get_contents($file); $sql = file_get_contents($file);
$pdo->exec($sql); $pdo->exec($sql);
@ -42,13 +40,9 @@ foreach ($all_files as $file) {
$stmt = $pdo->prepare("INSERT INTO schema_migrations (version) VALUES (?)"); $stmt = $pdo->prepare("INSERT INTO schema_migrations (version) VALUES (?)");
$stmt->execute([$filename]); $stmt->execute([$filename]);
$pdo->commit();
echo " Success.\n"; echo " Success.\n";
} catch (PDOException $e) { } catch (PDOException $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
echo " Error applying migration $filename: " . $e->getMessage() . "\n"; echo " Error applying migration $filename: " . $e->getMessage() . "\n";
// Stop on first error // Stop on first error
break; break;

View File

@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS `product_materials` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`product_id` INT NOT NULL,
`material_type` ENUM('pdf', 'image', 'youtube') NOT NULL,
`title` VARCHAR(255) NOT NULL,
`path` VARCHAR(255),
`url` VARCHAR(255),
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`product_id`) REFERENCES `products`(`id`) ON DELETE CASCADE
);

View File

@ -14864,3 +14864,318 @@ Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84
Found product price. Net: 233.2, Gross: 286.84 Found product price. Net: 233.2, Gross: 286.84
FINAL: Returning Net: 233.2, Gross: 286.84 FINAL: Returning Net: 233.2, Gross: 286.84
--- ---
---
START getEffectivePrice for product 1, client 1
Client price query executed. Found: {"price_net":"837.40","price_gross":"1030.00"}
Found client price. Net: 837.4, Gross: 1030
FINAL: Returning Net: 837.4, Gross: 1030
---
---
START getEffectivePrice for product 2, client 1
Client price query executed. Found: {"price_net":"1056.91","price_gross":"1300.00"}
Found client price. Net: 1056.91, Gross: 1300
FINAL: Returning Net: 1056.91, Gross: 1300
---
---
START getEffectivePrice for product 3, client 1
Client price query executed. Found: {"price_net":"32.52","price_gross":"40.00"}
Found client price. Net: 32.52, Gross: 40
FINAL: Returning Net: 32.52, Gross: 40
---
---
START getEffectivePrice for product 4, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 5, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"68.00","price_gross":"83.64"}
Found product price. Net: 68, Gross: 83.64
FINAL: Returning Net: 68, Gross: 83.64
---
---
START getEffectivePrice for product 6, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"171.60","price_gross":"211.07"}
Found product price. Net: 171.6, Gross: 211.07
FINAL: Returning Net: 171.6, Gross: 211.07
---
---
START getEffectivePrice for product 7, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84"}
Found product price. Net: 233.2, Gross: 286.84
FINAL: Returning Net: 233.2, Gross: 286.84
---
---
START getEffectivePrice for product 1, client 1
Client price query executed. Found: {"price_net":"837.40","price_gross":"1030.00"}
Found client price. Net: 837.4, Gross: 1030
FINAL: Returning Net: 837.4, Gross: 1030
---
---
START getEffectivePrice for product 2, client 1
Client price query executed. Found: {"price_net":"1056.91","price_gross":"1300.00"}
Found client price. Net: 1056.91, Gross: 1300
FINAL: Returning Net: 1056.91, Gross: 1300
---
---
START getEffectivePrice for product 3, client 1
Client price query executed. Found: {"price_net":"32.52","price_gross":"40.00"}
Found client price. Net: 32.52, Gross: 40
FINAL: Returning Net: 32.52, Gross: 40
---
---
START getEffectivePrice for product 4, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 5, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"68.00","price_gross":"83.64"}
Found product price. Net: 68, Gross: 83.64
FINAL: Returning Net: 68, Gross: 83.64
---
---
START getEffectivePrice for product 6, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"171.60","price_gross":"211.07"}
Found product price. Net: 171.6, Gross: 211.07
FINAL: Returning Net: 171.6, Gross: 211.07
---
---
START getEffectivePrice for product 7, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84"}
Found product price. Net: 233.2, Gross: 286.84
FINAL: Returning Net: 233.2, Gross: 286.84
---
---
START getEffectivePrice for product 1, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"1111.00","price_gross":"1366.53"}
Found product price. Net: 1111, Gross: 1366.53
FINAL: Returning Net: 1111, Gross: 1366.53
---
---
START getEffectivePrice for product 2, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"1318.05","price_gross":"1621.20"}
Found product price. Net: 1318.05, Gross: 1621.2
FINAL: Returning Net: 1318.05, Gross: 1621.2
---
---
START getEffectivePrice for product 3, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 4, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 5, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"68.00","price_gross":"83.64"}
Found product price. Net: 68, Gross: 83.64
FINAL: Returning Net: 68, Gross: 83.64
---
---
START getEffectivePrice for product 6, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"171.60","price_gross":"211.07"}
Found product price. Net: 171.6, Gross: 211.07
FINAL: Returning Net: 171.6, Gross: 211.07
---
---
START getEffectivePrice for product 7, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84"}
Found product price. Net: 233.2, Gross: 286.84
FINAL: Returning Net: 233.2, Gross: 286.84
---
---
START getEffectivePrice for product 2, client 1
Client price query executed. Found: {"price_net":"1056.91","price_gross":"1300.00"}
Found client price. Net: 1056.91, Gross: 1300
FINAL: Returning Net: 1056.91, Gross: 1300
---
---
START getEffectivePrice for product 1, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"1111.00","price_gross":"1366.53"}
Found product price. Net: 1111, Gross: 1366.53
FINAL: Returning Net: 1111, Gross: 1366.53
---
---
START getEffectivePrice for product 2, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"1318.05","price_gross":"1621.20"}
Found product price. Net: 1318.05, Gross: 1621.2
FINAL: Returning Net: 1318.05, Gross: 1621.2
---
---
START getEffectivePrice for product 3, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 4, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 5, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"68.00","price_gross":"83.64"}
Found product price. Net: 68, Gross: 83.64
FINAL: Returning Net: 68, Gross: 83.64
---
---
START getEffectivePrice for product 6, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"171.60","price_gross":"211.07"}
Found product price. Net: 171.6, Gross: 211.07
FINAL: Returning Net: 171.6, Gross: 211.07
---
---
START getEffectivePrice for product 7, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84"}
Found product price. Net: 233.2, Gross: 286.84
FINAL: Returning Net: 233.2, Gross: 286.84
---
---
START getEffectivePrice for product 2, client 1
Client price query executed. Found: {"price_net":"1056.91","price_gross":"1300.00"}
Found client price. Net: 1056.91, Gross: 1300
FINAL: Returning Net: 1056.91, Gross: 1300
---
---
START getEffectivePrice for product 1, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"1111.00","price_gross":"1366.53"}
Found product price. Net: 1111, Gross: 1366.53
FINAL: Returning Net: 1111, Gross: 1366.53
---
---
START getEffectivePrice for product 2, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"1318.05","price_gross":"1621.20"}
Found product price. Net: 1318.05, Gross: 1621.2
FINAL: Returning Net: 1318.05, Gross: 1621.2
---
---
START getEffectivePrice for product 3, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 4, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 5, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"68.00","price_gross":"83.64"}
Found product price. Net: 68, Gross: 83.64
FINAL: Returning Net: 68, Gross: 83.64
---
---
START getEffectivePrice for product 6, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"171.60","price_gross":"211.07"}
Found product price. Net: 171.6, Gross: 211.07
FINAL: Returning Net: 171.6, Gross: 211.07
---
---
START getEffectivePrice for product 7, client
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84"}
Found product price. Net: 233.2, Gross: 286.84
FINAL: Returning Net: 233.2, Gross: 286.84
---
---
START getEffectivePrice for product 1, client 1
Client price query executed. Found: {"price_net":"837.40","price_gross":"1030.00"}
Found client price. Net: 837.4, Gross: 1030
FINAL: Returning Net: 837.4, Gross: 1030
---
---
START getEffectivePrice for product 2, client 1
Client price query executed. Found: {"price_net":"1056.91","price_gross":"1300.00"}
Found client price. Net: 1056.91, Gross: 1300
FINAL: Returning Net: 1056.91, Gross: 1300
---
---
START getEffectivePrice for product 3, client 1
Client price query executed. Found: {"price_net":"32.52","price_gross":"40.00"}
Found client price. Net: 32.52, Gross: 40
FINAL: Returning Net: 32.52, Gross: 40
---
---
START getEffectivePrice for product 4, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"9.95","price_gross":"12.24"}
Found product price. Net: 9.95, Gross: 12.24
FINAL: Returning Net: 9.95, Gross: 12.24
---
---
START getEffectivePrice for product 5, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"68.00","price_gross":"83.64"}
Found product price. Net: 68, Gross: 83.64
FINAL: Returning Net: 68, Gross: 83.64
---
---
START getEffectivePrice for product 6, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"171.60","price_gross":"211.07"}
Found product price. Net: 171.6, Gross: 211.07
FINAL: Returning Net: 171.6, Gross: 211.07
---
---
START getEffectivePrice for product 7, client 1
Client price query executed. Found: No
Client price not found or not set, falling back to product price.
Product price query executed. Found: {"price_net":"233.20","price_gross":"286.84"}
Found product price. Net: 233.2, Gross: 286.84
FINAL: Returning Net: 233.2, Gross: 286.84
---
---
START getEffectivePrice for product 1, client 1
Client price query executed. Found: {"price_net":"837.40","price_gross":"1030.00"}
Found client price. Net: 837.4, Gross: 1030
FINAL: Returning Net: 837.4, Gross: 1030
---

View File

@ -204,6 +204,9 @@ $translations = [
'pdf_product' => 'Produkt', 'pdf_product' => 'Produkt',
'pdf_quantity' => 'Ilość', 'pdf_quantity' => 'Ilość',
'pdf_price' => 'Cena', 'pdf_price' => 'Cena',
'additional_materials_tab' => 'Wytyczne projektowe i dla wykonawców',
'no_additional_materials' => 'Brak dodatkowych materiałów.',
'invalid_youtube_url' => 'Nieprawidłowy adres URL YouTube.',
], ],
'en' => [ 'en' => [
'login_header' => 'Login', 'login_header' => 'Login',
@ -405,6 +408,9 @@ $translations = [
'pdf_product' => 'Product', 'pdf_product' => 'Product',
'pdf_quantity' => 'Quantity', 'pdf_quantity' => 'Quantity',
'pdf_price' => 'Price', 'pdf_price' => 'Price',
'additional_materials_tab' => 'Additional Materials',
'no_additional_materials' => 'No additional materials available.',
'invalid_youtube_url' => 'Invalid YouTube URL.',
], ],
]; ];

View File

@ -30,6 +30,11 @@ try {
$product_images = $img_stmt->fetchAll(PDO::FETCH_ASSOC); $product_images = $img_stmt->fetchAll(PDO::FETCH_ASSOC);
$primary_image = $product_images[0] ?? null; $primary_image = $product_images[0] ?? null;
// Fetch product materials
$materials_stmt = $pdo->prepare("SELECT * FROM product_materials WHERE product_id = ? ORDER BY created_at ASC");
$materials_stmt->execute([$product_id]);
$product_materials = $materials_stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) { } catch (PDOException $e) {
die(t('db_connection_error') . $e->getMessage()); die(t('db_connection_error') . $e->getMessage());
} }
@ -110,6 +115,9 @@ require_once __DIR__ . '/includes/header.php';
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" id="related-tab" data-bs-toggle="tab" data-bs-target="#related" type="button" role="tab" aria-controls="related" aria-selected="false"><?= t('related_products_tab') ?></button> <button class="nav-link" id="related-tab" data-bs-toggle="tab" data-bs-target="#related" type="button" role="tab" aria-controls="related" aria-selected="false"><?= t('related_products_tab') ?></button>
</li> </li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="materials-tab" data-bs-toggle="tab" data-bs-target="#materials" type="button" role="tab" aria-controls="materials" aria-selected="false"><?= t('additional_materials_tab') ?></button>
</li>
</ul> </ul>
<div class="tab-content p-3 border border-top-0" id="productTabsContent"> <div class="tab-content p-3 border border-top-0" id="productTabsContent">
<div class="tab-pane fade show active" id="description" role="tabpanel" aria-labelledby="description-tab"> <div class="tab-pane fade show active" id="description" role="tabpanel" aria-labelledby="description-tab">
@ -190,6 +198,40 @@ require_once __DIR__ . '/includes/header.php';
?> ?>
</div> </div>
</div> </div>
<div class="tab-pane fade" id="materials" role="tabpanel" aria-labelledby="materials-tab">
<?php
if ($product_materials) {
echo '<div class="list-group list-group-flush">';
foreach ($product_materials as $material) {
echo '<div class="list-group-item">';
echo '<h5>' . htmlspecialchars($material['title']) . '</h5>';
switch ($material['material_type']) {
case 'pdf':
echo '<a href="' . BASE_URL . 'uploads/products/' . htmlspecialchars($material['path']) . '" download><i class="bi bi-file-earmark-arrow-down"></i> Pobierz PDF</a>';
break;
case 'image':
echo '<img src="' . BASE_URL . 'uploads/products/' . htmlspecialchars($material['path']) . '" class="img-fluid rounded shadow-sm">';
break;
case 'youtube':
$video_id = '';
if (preg_match('/(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:embed\/|watch\?v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/', $material['url'], $matches)) {
$video_id = $matches[1];
}
if ($video_id) {
echo '<div class="ratio ratio-16x9"><iframe src="https://www.youtube.com/embed/' . $video_id . '" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div>';
} else {
echo '<p>' . t('invalid_youtube_url') . '</p>';
}
break;
}
echo '</div>';
}
echo '</div>';
} else {
echo '<p class="mb-0">' . t('no_additional_materials') . '</p>';
}
?>
</div>
</div> </div>
</div> </div>
</main> </main>