query("SELECT * FROM brands WHERE status = 1")->fetchAll(); if ($_SERVER['REQUEST_METHOD'] == 'POST') { if (verify_csrf($_POST[CSRF_TOKEN_NAME] ?? '')) { $name = trim($_POST['name']); $slug = create_slug($name) . '-' . uniqid(); $category_id = intval($_POST['category_id']); $subcategory_id = !empty($_POST['subcategory_id']) ? intval($_POST['subcategory_id']) : null; $brand_id = !empty($_POST['brand_id']) ? intval($_POST['brand_id']) : null; $short_description = trim($_POST['short_description'] ?? ''); $description = trim($_POST['description'] ?? ''); $specifications = trim($_POST['specifications'] ?? ''); $regular_price = floatval($_POST['regular_price']); $sale_price = !empty($_POST['sale_price']) ? floatval($_POST['sale_price']) : null; $sku = trim($_POST['sku'] ?? ''); $sku = $sku !== '' ? $sku : null; // NULL instead of empty string to avoid UNIQUE conflicts $stock_quantity = intval($_POST['stock_quantity'] ?? 0); $stock_status = $_POST['stock_status'] ?? 'in_stock'; $tags = trim($_POST['tags'] ?? ''); $meta_title = trim($_POST['meta_title'] ?? ''); $meta_description = trim($_POST['meta_description'] ?? ''); $featured = isset($_POST['featured']) ? 1 : 0; $best_seller = isset($_POST['best_seller']) ? 1 : 0; $new_arrival = isset($_POST['new_arrival']) ? 1 : 0; $discount = 0; if ($sale_price && $regular_price > 0) { $discount = (int)round((($regular_price - $sale_price) / $regular_price) * 100); } // Handle thumbnail upload $thumbnail = ''; if (isset($_FILES['thumbnail']) && $_FILES['thumbnail']['error'] == 0) { $thumbnail = upload_image($_FILES['thumbnail'], 'products', $name); if (!$thumbnail) { $error = 'Image upload failed. Please check that the file is JPG/PNG/GIF/WebP and under 5MB, and that assets/uploads/products/ folder is writable.'; } } elseif (isset($_FILES['thumbnail']) && $_FILES['thumbnail']['error'] != 0 && $_FILES['thumbnail']['error'] != 4) { // Error 4 = no file uploaded, that's fine $error = 'Image upload error (code: ' . $_FILES['thumbnail']['error'] . '). Please try again.'; } if (!isset($error)) { try { $db->beginTransaction(); $stmt = $db->prepare("INSERT INTO products (category_id, subcategory_id, brand_id, name, slug, short_description, description, specifications, regular_price, sale_price, discount_percent, sku, stock_quantity, stock_status, featured, best_seller, new_arrival, thumbnail, tags, meta_title, meta_description) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$category_id, $subcategory_id, $brand_id, $name, $slug, $short_description, $description, $specifications, $regular_price, $sale_price, $discount, $sku, $stock_quantity, $stock_status, $featured, $best_seller, $new_arrival, $thumbnail, $tags, $meta_title, $meta_description]); $product_id = $db->lastInsertId(); // Handle additional images if (isset($_FILES['images']) && !empty($_FILES['images']['name'][0])) { $count = count($_FILES['images']['name']); for ($i = 0; $i < $count; $i++) { if ($_FILES['images']['error'][$i] == 0) { $file = [ 'name' => $_FILES['images']['name'][$i], 'type' => $_FILES['images']['type'][$i], 'tmp_name' => $_FILES['images']['tmp_name'][$i], 'error' => $_FILES['images']['error'][$i], 'size' => $_FILES['images']['size'][$i] ]; $image = upload_image($file, 'products', $name . '-' . $i); if ($image) { $db->prepare("INSERT INTO product_images (product_id, image, sort_order) VALUES (?, ?, ?)") ->execute([$product_id, $image, $i]); } } } } $db->commit(); admin_set_flash('success', 'Product added successfully'); header('Location: ' . ADMIN_PATH . 'pages/products.php'); exit; } catch (Exception $e) { if ($db->inTransaction()) { $db->rollBack(); } $error = 'Failed to add product: ' . $e->getMessage(); } } } else { $error = 'Security token expired. Please refresh the page and try again.'; } } ?>
Basic Information
Pricing & Inventory
Visibility

Product Images
You can select multiple images (JPG, PNG, GIF, WebP — max 5MB each)

SEO & Tags

Cancel