diff --git a/admin/edit_product.php b/admin/edit_product.php index 6af4bcc..c9f8f56 100644 --- a/admin/edit_product.php +++ b/admin/edit_product.php @@ -50,6 +50,29 @@ if (isset($_GET['delete_document']) && isset($_GET['id'])) { 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 = [ 'id' => null, @@ -104,6 +127,11 @@ if (isset($_GET['id'])) { $docs_stmt->execute([$product_id]); $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 = ?"); @@ -418,12 +500,60 @@ $page_title = $product['id'] ? 'Edytuj produkt' : 'Dodaj produkt'; endif; ?> +
+
Dodatkowe materiały
+
+ + +
+
+ + +
+
+ + +
+ + '; + foreach($product_materials as $material) { + echo '
  • '; + echo htmlspecialchars($material['title']) . ' (' . htmlspecialchars($material['material_type']) . ')'; + echo 'Usuń'; + echo '
  • '; + } + echo ''; + endif; ?> +
    +
    Anuluj
    + \ No newline at end of file diff --git a/db/migrate.php b/db/migrate.php index 87e38a9..8591942 100644 --- a/db/migrate.php +++ b/db/migrate.php @@ -33,8 +33,6 @@ foreach ($all_files as $file) { echo "Applying migration: $filename\n"; try { - $pdo->beginTransaction(); - $sql = file_get_contents($file); $pdo->exec($sql); @@ -42,13 +40,9 @@ foreach ($all_files as $file) { $stmt = $pdo->prepare("INSERT INTO schema_migrations (version) VALUES (?)"); $stmt->execute([$filename]); - $pdo->commit(); echo " Success.\n"; } catch (PDOException $e) { - if ($pdo->inTransaction()) { - $pdo->rollBack(); - } echo " Error applying migration $filename: " . $e->getMessage() . "\n"; // Stop on first error break; diff --git a/db/migrations/038_create_product_materials_table.sql b/db/migrations/038_create_product_materials_table.sql new file mode 100644 index 0000000..ce44836 --- /dev/null +++ b/db/migrations/038_create_product_materials_table.sql @@ -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 +); diff --git a/debug_price.log b/debug_price.log index e4ecb4a..5eed4b5 100644 --- a/debug_price.log +++ b/debug_price.log @@ -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 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 +--- diff --git a/includes/i18n.php b/includes/i18n.php index 128c537..bb58bd3 100644 --- a/includes/i18n.php +++ b/includes/i18n.php @@ -204,6 +204,9 @@ $translations = [ 'pdf_product' => 'Produkt', 'pdf_quantity' => 'Ilość', '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' => [ 'login_header' => 'Login', @@ -405,6 +408,9 @@ $translations = [ 'pdf_product' => 'Product', 'pdf_quantity' => 'Quantity', 'pdf_price' => 'Price', + 'additional_materials_tab' => 'Additional Materials', + 'no_additional_materials' => 'No additional materials available.', + 'invalid_youtube_url' => 'Invalid YouTube URL.', ], ]; diff --git a/product.php b/product.php index 1cdf399..48c0b67 100644 --- a/product.php +++ b/product.php @@ -30,6 +30,11 @@ try { $product_images = $img_stmt->fetchAll(PDO::FETCH_ASSOC); $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) { die(t('db_connection_error') . $e->getMessage()); } @@ -110,6 +115,9 @@ require_once __DIR__ . '/includes/header.php'; +
    @@ -190,6 +198,40 @@ require_once __DIR__ . '/includes/header.php'; ?>
    +
    + '; + foreach ($product_materials as $material) { + echo '
    '; + echo '
    ' . htmlspecialchars($material['title']) . '
    '; + switch ($material['material_type']) { + case 'pdf': + echo ' Pobierz PDF'; + break; + case 'image': + echo ''; + 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 '
    '; + } else { + echo '

    ' . t('invalid_youtube_url') . '

    '; + } + break; + } + echo '
    '; + } + echo '
    '; + } else { + echo '

    ' . t('no_additional_materials') . '

    '; + } + ?> + diff --git a/uploads/products/1/materials/mat_1_6960f4a16e9324.95735517.pdf b/uploads/products/1/materials/mat_1_6960f4a16e9324.95735517.pdf new file mode 100644 index 0000000..f288936 Binary files /dev/null and b/uploads/products/1/materials/mat_1_6960f4a16e9324.95735517.pdf differ