| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846 |
- <?php
- // Αρχείο: /var/www/html/hockey-cms/admin/products.php
- require_once 'admin_auth.php';
- require_once '../config/database.php';
- // --- Μηχανισμός Αλλαγής Γλώσσας ---
- if (isset($_GET['lang']) && in_array($_GET['lang'], ['el', 'en'])) {
- $_SESSION['lang'] = $_GET['lang'];
- }
- $current_lang = $_SESSION['lang'] ?? 'el';
- $translations = [
- 'el' => [
- 'page_title' => 'Διαχείριση Προϊόντων - Hockey CMS',
- 'header_title' => 'Διαχείριση Προϊόντων',
- 'attr_cat_title' => 'Χαρακτηριστικά Κατηγορίας',
- 'attr_placeholder' => 'π.χ. συμπληρώστε τιμή...',
- 'edit_prod_title' => 'Επεξεργασία Προϊόντος #',
- 'add_prod_title' => 'Προσθήκη Νέου Προϊόντος',
- 'prod_title' => 'Τίτλος Προϊόντος *',
- 'category' => 'Κατηγορία *',
- 'select_cat' => '-- Επίλεξε --',
- 'sku' => 'Κωδικός / Αύξων Αριθμός (SKU)',
- 'price' => 'Τιμή (€) *',
- 'stock' => 'Απόθεμα',
- 'status' => 'Κατάσταση',
- 'status_avail' => 'Διαθέσιμο (Available)',
- 'status_sold' => 'Πουλήθηκε (Sold)',
- 'status_hidden' => 'Απόκρυψη',
- 'seller_assign' => 'Ιδιοκτήτης / Πωλητής *',
- 'tags' => 'Ετικέτες Αναζήτησης (Tags)',
- 'tags_placeholder' => 'Πληκτρολογήστε λέξη και πατήστε Enter...',
- 'tags_hint' => 'Πατήστε Enter ή Κόμμα (,) για να δημιουργήσετε tag.',
- 'desc' => 'Περιγραφή',
- 'photo' => 'Κεντρική Φωτογραφία',
- 'photo_hint_edit' => '(Αφήστε το κενό αν δεν θέλετε να την αλλάξετε)',
- 'photo_hint_add' => '(Αυτόματο resize)',
- 'btn_update' => 'Ενημέρωση',
- 'btn_add' => 'Προσθήκη',
- 'btn_cancel' => 'Ακύρωση',
- 'list_title' => 'Λίστα Προϊόντων',
- 'th_id_sku' => 'ID / SKU',
- 'th_photo' => 'Φωτογραφία',
- 'th_title' => 'Τίτλος / Tags',
- 'th_cat' => 'Κατηγορία',
- 'th_price' => 'Τιμή',
- 'th_stock' => 'Απόθεμα',
- 'th_status' => 'Κατάσταση',
- 'th_actions' => 'Ενέργειες',
- 'no_img' => 'No IMG',
- 'pcs' => 'τεμ.',
- 'btn_view' => 'Προβολή',
- 'btn_del' => 'Διαγραφή',
- 'confirm_del' => 'Είστε σίγουρος ότι θέλετε να διαγράψετε αυτό το προϊόν;',
- 'modal_id' => 'Κωδικός (ID):',
- 'modal_sku' => 'SKU:',
- 'modal_cat' => 'Κατηγορία:',
- 'modal_price' => 'Τιμή:',
- 'modal_stock' => 'Απόθεμα:',
- 'modal_status' => 'Κατάσταση:',
- 'modal_seller' => 'Πωλητής:',
- 'modal_tags' => 'Tags:',
- 'modal_desc' => 'Περιγραφή Αγγελίας:',
- 'modal_no_attrs' => 'Δεν υπάρχουν επιπλέον χαρακτηριστικά',
- 'msg_add_ok' => 'Το προϊόν προστέθηκε επιτυχώς!',
- 'msg_edit_ok' => 'Το προϊόν ενημερώθηκε επιτυχώς!',
- 'msg_err_req' => 'Παρακαλώ συμπληρώστε τα υποχρεωτικά πεδία.',
- 'msg_err_del_orders' => 'Δεν μπορείτε να διαγράψετε αυτό το προϊόν διότι υπάρχει στο ιστορικό παραγγελιών. Αν δεν θέλετε να φαίνεται, αλλάξτε την κατάστασή του σε "Απόκρυψη".',
- 'msg_del_ok' => 'Το προϊόν διαγράφηκε επιτυχώς!'
- ],
- 'en' => [
- 'page_title' => 'Products Management - Hockey CMS',
- 'header_title' => 'Products Management',
- 'attr_cat_title' => 'Category Attributes',
- 'attr_placeholder' => 'e.g. fill in value...',
- 'edit_prod_title' => 'Edit Product #',
- 'add_prod_title' => 'Add New Product',
- 'prod_title' => 'Product Title *',
- 'category' => 'Category *',
- 'select_cat' => '-- Select --',
- 'sku' => 'Code / Sequential (SKU)',
- 'price' => 'Price (€) *',
- 'stock' => 'Stock',
- 'status' => 'Status',
- 'status_avail' => 'Available',
- 'status_sold' => 'Sold',
- 'status_hidden' => 'Hidden',
- 'seller_assign' => 'Owner / Seller *',
- 'tags' => 'Search Tags',
- 'tags_placeholder' => 'Type a word and press Enter...',
- 'tags_hint' => 'Press Enter or Comma (,) to create a tag.',
- 'desc' => 'Description',
- 'photo' => 'Main Photo',
- 'photo_hint_edit' => '(Leave empty to keep current)',
- 'photo_hint_add' => '(Auto resize)',
- 'btn_update' => 'Update',
- 'btn_add' => 'Add',
- 'btn_cancel' => 'Cancel',
- 'list_title' => 'Products List',
- 'th_id_sku' => 'ID / SKU',
- 'th_photo' => 'Photo',
- 'th_title' => 'Title / Tags',
- 'th_cat' => 'Category',
- 'th_price' => 'Price',
- 'th_stock' => 'Stock',
- 'th_status' => 'Status',
- 'th_actions' => 'Actions',
- 'no_img' => 'No IMG',
- 'pcs' => 'pcs.',
- 'btn_view' => 'View',
- 'btn_del' => 'Delete',
- 'confirm_del' => 'Are you sure you want to delete this product?',
- 'modal_id' => 'ID:',
- 'modal_sku' => 'SKU:',
- 'modal_cat' => 'Category:',
- 'modal_price' => 'Price:',
- 'modal_stock' => 'Stock:',
- 'modal_status' => 'Status:',
- 'modal_seller' => 'Seller:',
- 'modal_tags' => 'Tags:',
- 'modal_desc' => 'Listing Description:',
- 'modal_no_attrs' => 'No additional attributes',
- 'msg_add_ok' => 'Product added successfully!',
- 'msg_edit_ok' => 'Product updated successfully!',
- 'msg_err_req' => 'Please fill in the required fields.',
- 'msg_err_del_orders' => 'Cannot delete product because it exists in order history. If you want to hide it, change status to "Hidden".',
- 'msg_del_ok' => 'Product deleted successfully!'
- ]
- ];
- $t = $translations[$current_lang];
- // --- 1. AJAX API: Φέρνει δυναμικά τα πεδία όταν αλλάζει η Κατηγορία ---
- if (isset($_GET['ajax_get_attributes']) && isset($_GET['category_id'])) {
- $cat_id = (int)$_GET['category_id'];
- $product_id = isset($_GET['product_id']) ? (int)$_GET['product_id'] : 0;
-
- $stmt = $pdo->prepare("SELECT attribute_group_id FROM categories WHERE id = ?");
- $stmt->execute([$cat_id]);
- $cat = $stmt->fetch();
-
- if ($cat && $cat['attribute_group_id']) {
- $attrStmt = $pdo->prepare("SELECT id, name, unit FROM attributes WHERE group_id = ? ORDER BY name");
- $attrStmt->execute([$cat['attribute_group_id']]);
- $attributes = $attrStmt->fetchAll();
-
- $existing_values = [];
- if ($product_id > 0) {
- $valStmt = $pdo->prepare("SELECT attribute_id, value FROM product_attribute_values WHERE product_id = ?");
- $valStmt->execute([$product_id]);
- while ($vRow = $valStmt->fetch(PDO::FETCH_ASSOC)) {
- $existing_values[$vRow['attribute_id']] = $vRow['value'];
- }
- }
-
- if ($attributes) {
- echo "<h4 style='margin-bottom: 12px; margin-top: 20px; color: var(--primary); text-transform: uppercase; font-size: 14px; letter-spacing: 0.5px;'>{$t['attr_cat_title']}</h4>";
- echo "<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 15px;'>";
- foreach ($attributes as $attr) {
- $val = isset($existing_values[$attr['id']]) ? htmlspecialchars($existing_values[$attr['id']]) : '';
- echo "<div class='form-group' style='margin-bottom:0;'>";
- echo "<label style='display:block; margin-bottom:8px; font-weight:700; font-size:13px; color:var(--text-muted); text-transform:uppercase;'>" . htmlspecialchars($attr['name']) . ($attr['unit'] ? " ({$attr['unit']})" : "") . "</label>";
- echo "<input type='text' name='attributes[" . $attr['id'] . "]' value='" . $val . "' placeholder='{$t['attr_placeholder']}' style='width:100%; padding:12px; background:#0f172a; border:1px solid var(--border-color); color:var(--text-main); border-radius:6px; box-sizing:border-box; font-family:Inter,sans-serif; outline:none;'>";
- echo "</div>";
- }
- echo "</div>";
- }
- }
- exit;
- }
- // --- 2. AJAX API: Δημιουργία προτεινόμενου SKU (Πρόθεμα 3 Γράμματα + Αύξων) ---
- if (isset($_GET['ajax_generate_sku']) && isset($_GET['category_id'])) {
- $cat_id = (int)$_GET['category_id'];
- $stmt = $pdo->prepare("SELECT name_en FROM categories WHERE id = ?");
- $stmt->execute([$cat_id]);
- $name_en = $stmt->fetchColumn();
- if ($name_en) {
- // Κρατάμε μόνο τα γράμματα από τα Αγγλικά
- $clean_name = preg_replace('/[^a-zA-Z]/', '', $name_en);
- $prefix = strtoupper(substr($clean_name, 0, 3));
- if (empty($prefix)) { $prefix = 'CAT'; }
- // Βρίσκουμε το μεγαλύτερο νούμερο με αυτό το πρόθεμα
- $stmtSku = $pdo->prepare("SELECT sku FROM products WHERE sku LIKE ?");
- $stmtSku->execute([$prefix . '-%']);
- $skus = $stmtSku->fetchAll(PDO::FETCH_COLUMN);
- $max = 0;
- foreach ($skus as $s) {
- $parts = explode('-', $s);
- if (count($parts) > 1) {
- $num = (int)end($parts);
- if ($num > $max) { $max = $num; }
- }
- }
- // Γεννάμε το νέο νούμερο με μηδενικά μπροστά (π.χ. 001, 002)
- $next_num = str_pad($max + 1, 3, '0', STR_PAD_LEFT);
- echo $prefix . '-' . $next_num;
- }
- exit;
- }
- header('Content-Type: text/html; charset=utf-8');
- $message = '';
- function resizeAndSaveImage($source_file, $target_file, $max_resolution = 800) {
- $info = getimagesize($source_file);
- if (!$info) return false;
- $mime = $info['mime'];
- switch ($mime) {
- case 'image/jpeg': $image = imagecreatefromjpeg($source_file); break;
- case 'image/png': $image = imagecreatefrompng($source_file); break;
- case 'image/gif': $image = imagecreatefromgif($source_file); break;
- default: return false;
- }
- $original_width = imagesx($image);
- $original_height = imagesy($image);
- $ratio = $max_resolution / max($original_width, $original_height);
- $new_width = $original_width > $max_resolution ? round($original_width * $ratio) : $original_width;
- $new_height = $original_height > $max_resolution ? round($original_height * $ratio) : $original_height;
- $new_image = imagecreatetruecolor($new_width, $new_height);
- if ($mime == 'image/png') {
- imagealphablending($new_image, false);
- imagesavealpha($new_image, true);
- }
- imagecopyresampled($new_image, $image, 0, 0, 0, 0, $new_width, $new_height, $original_width, $original_height);
- imagejpeg($new_image, $target_file, 85);
- imagedestroy($image);
- imagedestroy($new_image);
- return true;
- }
- if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
- $action = $_POST['action'];
- $title = trim($_POST['title']);
- $sku = trim($_POST['sku']);
- $category_id = (int)$_POST['category_id'];
- $price = (float)$_POST['price'];
- $quantity = (int)$_POST['quantity'];
- $status = $_POST['status'] ?? 'available';
- $description = trim($_POST['description']);
- $search_tags = trim($_POST['search_tags']);
-
- $seller_id = isset($_POST['seller_id']) ? (int)$_POST['seller_id'] : $_SESSION['user_id'];
- if (!empty($title) && !empty($category_id) && !empty($price)) {
- try {
- $pdo->beginTransaction();
- $stmtGrp = $pdo->prepare("SELECT attribute_group_id FROM categories WHERE id = ?");
- $stmtGrp->execute([$category_id]);
- $group_id = $stmtGrp->fetchColumn();
- if ($action == 'add_product') {
- $stmt = $pdo->prepare("INSERT INTO products (seller_id, category_id, attribute_group_id, title, sku, description, search_tags, price, quantity, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
- $stmt->execute([$seller_id, $category_id, $group_id, $title, $sku, $description, $search_tags, $price, $quantity, $status]);
- $product_id = $pdo->lastInsertId();
- $message_text = $t['msg_add_ok'];
- } elseif ($action == 'edit_product') {
- $product_id = (int)$_POST['product_id'];
- $stmt = $pdo->prepare("UPDATE products SET seller_id = ?, category_id = ?, attribute_group_id = ?, title = ?, sku = ?, description = ?, search_tags = ?, price = ?, quantity = ?, status = ? WHERE id = ?");
- $stmt->execute([$seller_id, $category_id, $group_id, $title, $sku, $description, $search_tags, $price, $quantity, $status, $product_id]);
-
- $delAttr = $pdo->prepare("DELETE FROM product_attribute_values WHERE product_id = ?");
- $delAttr->execute([$product_id]);
- $message_text = $t['msg_edit_ok'];
- }
- if (isset($_POST['attributes']) && is_array($_POST['attributes'])) {
- $insAttr = $pdo->prepare("INSERT INTO product_attribute_values (product_id, attribute_id, value) VALUES (?, ?, ?)");
- foreach ($_POST['attributes'] as $attr_id => $val) {
- $val = trim($val);
- if ($val !== '') {
- $insAttr->execute([$product_id, $attr_id, $val]);
- }
- }
- }
- if (isset($_FILES['image']) && $_FILES['image']['error'] == 0) {
- $upload_dir = '../uploads/equipment/';
- if (!is_dir($upload_dir)) { mkdir($upload_dir, 0755, true); }
- $filename = 'prod_' . $product_id . '_' . time() . '.jpg';
- $target_path = $upload_dir . $filename;
- if (resizeAndSaveImage($_FILES['image']['tmp_name'], $target_path)) {
- $checkImg = $pdo->prepare("SELECT id FROM product_images WHERE product_id = ? AND is_primary = 1");
- $checkImg->execute([$product_id]);
- if ($checkImg->fetch()) {
- $updImg = $pdo->prepare("UPDATE product_images SET image_path = ? WHERE product_id = ? AND is_primary = 1");
- $updImg->execute(['uploads/equipment/' . $filename, $product_id]);
- } else {
- $insImg = $pdo->prepare("INSERT INTO product_images (product_id, image_path, is_primary) VALUES (?, ?, 1)");
- $insImg->execute([$product_id, 'uploads/equipment/' . $filename]);
- }
- }
- }
- $pdo->commit();
- $message = "<div class='alert success'>{$message_text}</div>";
- } catch (Exception $e) {
- $pdo->rollBack();
- $message = "<div class='alert error'>Σφάλμα: " . $e->getMessage() . "</div>";
- }
- } else {
- $message = "<div class='alert error'>{$t['msg_err_req']}</div>";
- }
- }
- if (isset($_GET['delete'])) {
- $del_id = (int)$_GET['delete'];
-
- $checkOrders = $pdo->prepare("SELECT COUNT(*) FROM order_items WHERE product_id = ?");
- $checkOrders->execute([$del_id]);
-
- if ($checkOrders->fetchColumn() > 0) {
- $message = "<div class='alert error'>{$t['msg_err_del_orders']}</div>";
- } else {
- try {
- $pdo->beginTransaction();
-
- $imgStmt = $pdo->prepare("SELECT image_path FROM product_images WHERE product_id = ?");
- $imgStmt->execute([$del_id]);
- $images = $imgStmt->fetchAll();
- foreach ($images as $img) {
- $imgPath = '../' . $img['image_path'];
- if (file_exists($imgPath) && is_file($imgPath)) { @unlink($imgPath); }
- }
-
- $pdo->prepare("DELETE FROM product_attribute_values WHERE product_id = ?")->execute([$del_id]);
- $pdo->prepare("DELETE FROM product_images WHERE product_id = ?")->execute([$del_id]);
- $pdo->prepare("DELETE FROM products WHERE id = ?")->execute([$del_id]);
-
- $pdo->commit();
- $message = "<div class='alert success'>{$t['msg_del_ok']}</div>";
- } catch (Exception $e) {
- $pdo->rollBack();
- $message = "<div class='alert error'>Σφάλμα: " . $e->getMessage() . "</div>";
- }
- }
- }
- $edit_product = null;
- if (isset($_GET['edit'])) {
- $edit_id = (int)$_GET['edit'];
- $editStmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
- $editStmt->execute([$edit_id]);
- $edit_product = $editStmt->fetch(PDO::FETCH_ASSOC);
- }
- // Φόρτωση Κατηγοριών
- $cat_col = ($current_lang === 'en') ? 'name_en' : 'name_el';
- $categories = $pdo->query("SELECT id, IFNULL(NULLIF(name_en, ''), name_el) as name FROM categories ORDER BY sort_order ASC, name_el ASC")->fetchAll();
- if ($current_lang === 'el') {
- $categories = $pdo->query("SELECT id, name_el as name FROM categories ORDER BY sort_order ASC, name_el ASC")->fetchAll();
- }
- // Φόρτωση όλων των χρηστών & Έξυπνη Εύρεση Ονόματος/Επιθέτου
- $all_users = $pdo->query("SELECT * FROM users ORDER BY username ASC")->fetchAll(PDO::FETCH_ASSOC);
- $user_map = [];
- foreach ($all_users as $u) {
- $fName = $u['first_name'] ?? $u['name'] ?? $u['firstname'] ?? '';
- $lName = $u['last_name'] ?? $u['surname'] ?? $u['lastname'] ?? '';
- $fullName = trim($fName . ' ' . $lName);
-
- $user_map[$u['id']] = $fullName ? $fullName . ' (' . $u['username'] . ')' : $u['username'];
- }
- // Λίστα προϊόντων
- $products = $pdo->query("SELECT p.id, p.sku, p.title, p.description, p.search_tags, p.price, p.quantity, p.status, p.category_id, p.seller_id, p.created_at,
- IFNULL(NULLIF(c.name_en, ''), c.name_el) as cat_name_en, c.name_el as cat_name_el, u.username, pi.image_path
- FROM products p
- LEFT JOIN categories c ON p.category_id = c.id
- LEFT JOIN users u ON p.seller_id = u.id
- LEFT JOIN product_images pi ON (p.id = pi.product_id AND pi.is_primary = 1)
- ORDER BY p.id DESC")->fetchAll(PDO::FETCH_ASSOC);
- $all_attrs = $pdo->query("SELECT pav.product_id, a.name, a.name_en, a.unit, pav.value
- FROM product_attribute_values pav
- JOIN attributes a ON pav.attribute_id = a.id")->fetchAll(PDO::FETCH_ASSOC);
- $attrs_by_product = [];
- foreach($all_attrs as $attr) {
- $attr['display_name'] = ($current_lang === 'en' && !empty($attr['name_en'])) ? $attr['name_en'] : $attr['name'];
- $attrs_by_product[$attr['product_id']][] = $attr;
- }
- foreach ($products as &$prod) {
- $prod['cat_name'] = ($current_lang === 'en' && !empty($prod['cat_name_en'])) ? $prod['cat_name_en'] : $prod['cat_name_el'];
- $prod['attributes'] = isset($attrs_by_product[$prod['id']]) ? $attrs_by_product[$prod['id']] : [];
-
- $prod['seller_display'] = isset($user_map[$prod['seller_id']]) ? $user_map[$prod['seller_id']] : $prod['username'];
- }
- unset($prod);
- ?>
- <!DOCTYPE html>
- <html lang="<?php echo $current_lang; ?>">
- <head>
- <meta charset="UTF-8">
- <title><?php echo $t['page_title']; ?></title>
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
- <style>
- :root {
- --primary: #f97316;
- --primary-hover: #ea580c;
- --bg-color: #0b1120;
- --panel-bg: #1e293b;
- --border-color: #334155;
- --text-main: #f8fafc;
- --text-muted: #94a3b8;
- }
- body { font-family: 'Inter', sans-serif; background-color: var(--bg-color); color: var(--text-main); margin: 0; }
- .content { margin-left: 250px; padding: 30px; }
- .content h2 { margin-top: 0; color: var(--primary); font-size: 26px; text-transform: uppercase; letter-spacing: 0.5px; }
-
- .card { background: var(--panel-bg); padding: 25px; border-radius: 8px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.3); border: 1px solid var(--border-color); margin-bottom: 25px; }
- .card h3 { margin-top: 0; color: var(--text-main); border-bottom: 1px solid rgba(255,255,255,0.05); padding-bottom: 15px; font-size: 18px; text-transform: uppercase; letter-spacing: 0.5px; }
-
- .form-group { margin-bottom: 20px; }
- .form-group label { display: block; margin-bottom: 8px; font-weight: 700; font-size: 13px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px;}
- .form-group input:not(#tag_input_field), .form-group select, .form-group textarea { width: 100%; padding: 12px; background: #0f172a; border: 1px solid var(--border-color); color: var(--text-main); border-radius: 6px; box-sizing: border-box; font-family: 'Inter', sans-serif; outline: none; transition: 0.2s; }
- .form-group input:focus, .form-group select:focus, .form-group textarea:focus { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(249, 115, 22, 0.2); }
-
- input[type="file"] { color: var(--text-main); font-family: 'Inter', sans-serif;}
- .btn { padding: 12px 20px; background: var(--primary); color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 800; font-size: 14px; text-transform: uppercase; transition: 0.2s; }
- .btn:hover { background: var(--primary-hover); transform: translateY(-1px); }
- .btn-secondary { background: rgba(255,255,255,0.05); color: var(--text-main); padding: 12px 20px; text-decoration: none; border-radius: 6px; display: inline-block; font-weight: 700; border: 1px solid var(--border-color); text-transform: uppercase; transition: 0.2s;}
- .btn-secondary:hover { background: rgba(255,255,255,0.1); }
-
- table { width: 100%; border-collapse: collapse; margin-top: 10px; }
- table th, table td { padding: 15px; border-bottom: 1px solid rgba(255,255,255,0.05); text-align: left; vertical-align: middle; }
- table th { background: rgba(0,0,0,0.2); color: var(--text-muted); text-transform: uppercase; font-size: 13px; font-weight: 700; }
- table td { color: var(--text-main); font-size: 14px; }
-
- .alert { padding: 15px; border-radius: 6px; margin-bottom: 20px; font-weight: 600; font-size: 14px;}
- .success { background: rgba(16, 185, 129, 0.1); color: #10b981; border: 1px solid rgba(16, 185, 129, 0.2); }
- .error { background: rgba(239, 68, 68, 0.1); color: #ef4444; border: 1px solid rgba(239, 68, 68, 0.2); }
-
- .thumb { width: 60px; height: 60px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border-color);}
-
- .badge { padding: 4px 10px; border-radius: 20px; font-size: 11px; font-weight: 800; color: #fff; text-transform: uppercase; }
- .badge-available { background: #10b981; }
- .badge-sold { background: #ef4444; }
- .badge-hidden { background: var(--text-muted); color: #0b1120; }
- .action-btn { background: rgba(59, 130, 246, 0.1); color: #3b82f6; border: 1px solid rgba(59, 130, 246, 0.2); padding: 8px 12px; border-radius: 4px; cursor: pointer; font-size: 12px; text-decoration: none; display: inline-block; font-weight: 700; text-transform: uppercase; transition: 0.2s;}
- .action-btn:hover { background: #3b82f6; color: white; }
- .btn-edit { background: rgba(249, 115, 22, 0.1); color: var(--primary); border: 1px solid rgba(249, 115, 22, 0.2); }
- .btn-edit:hover { background: var(--primary); color: white; }
- .btn-delete { background: rgba(239, 68, 68, 0.1); color: #ef4444; border: 1px solid rgba(239, 68, 68, 0.2); }
- .btn-delete:hover { background: #ef4444; color: white; }
- /* Tags Input Styles */
- .tags-input-container { display: flex; flex-wrap: wrap; gap: 5px; padding: 8px 10px; border: 1px solid var(--border-color); border-radius: 6px; background: #0f172a; align-items: center; cursor: text; }
- .tags-input-container input { border: none; outline: none; flex-grow: 1; padding: 4px; min-width: 150px; font-family: inherit; font-size: 14px; background: transparent; color: var(--text-main);}
- .tag-chip { background: rgba(255,255,255,0.1); color: var(--text-main); padding: 4px 10px; border-radius: 16px; font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 6px; border: 1px solid rgba(255,255,255,0.05);}
- .tag-chip span { cursor: pointer; color: var(--text-muted); font-weight: bold; font-size: 15px; line-height: 1; transition: 0.2s;}
- .tag-chip span:hover { color: #ef4444; }
- .modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.8); }
- .modal-content { background-color: var(--panel-bg); margin: 5% auto; padding: 30px; border-radius: 8px; width: 80%; max-width: 700px; position: relative; box-shadow: 0 10px 25px rgba(0,0,0,0.5); border: 1px solid var(--border-color);}
- .close-btn { position: absolute; right: 20px; top: 15px; font-size: 28px; font-weight: bold; cursor: pointer; color: var(--text-muted); }
- .close-btn:hover { color: var(--text-main); }
- .modal-body { display: flex; gap: 30px; margin-top: 20px; }
- .modal-body img { max-width: 100%; border-radius: 8px; border: 1px solid var(--border-color); }
- .modal-info p { margin: 8px 0; font-size: 14px; color: var(--text-muted);}
- .modal-info p strong { color: var(--text-main); font-weight: 600;}
- .modal-desc { background: rgba(0,0,0,0.2); padding: 15px; border-radius: 6px; border: 1px solid var(--border-color); margin-top: 15px; white-space: pre-wrap; font-size: 14px; color: var(--text-main); line-height: 1.5;}
- .modal-attr-list { list-style: none; padding: 0; margin: 15px 0; border-top: 1px solid rgba(255,255,255,0.05); padding-top: 15px; }
- .modal-attr-list li { margin-bottom: 8px; font-size: 14px; color: var(--text-muted);}
- .modal-attr-list li strong { color: var(--text-main); font-weight: 600;}
- </style>
- </head>
- <body>
-
- <?php include 'sidebar.php'; ?>
- <div class="content">
- <h2><?php echo $t['header_title']; ?></h2>
- <?php echo $message; ?>
- <div class="card">
- <h3><?php echo $edit_product ? $t['edit_prod_title'] . $edit_product['id'] : $t['add_prod_title']; ?></h3>
- <form method="POST" action="products.php" enctype="multipart/form-data" id="product_form">
- <input type="hidden" name="action" value="<?php echo $edit_product ? 'edit_product' : 'add_product'; ?>">
- <?php if ($edit_product): ?>
- <input type="hidden" name="product_id" value="<?php echo $edit_product['id']; ?>">
- <?php endif; ?>
-
- <div style="display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 20px;">
- <div class="form-group">
- <label><?php echo $t['prod_title']; ?></label>
- <input type="text" name="title" value="<?php echo $edit_product ? htmlspecialchars($edit_product['title']) : ''; ?>" required>
- </div>
- <div class="form-group">
- <label><?php echo $t['category']; ?></label>
- <select name="category_id" id="category_select" required onchange="loadAttributes(this.value); generateSku(this.value);">
- <option value=""><?php echo $t['select_cat']; ?></option>
- <?php foreach($categories as $cat): ?>
- <option value="<?php echo $cat['id']; ?>" <?php echo ($edit_product && $edit_product['category_id'] == $cat['id']) ? 'selected' : ''; ?>>
- <?php echo htmlspecialchars($cat['name']); ?>
- </option>
- <?php endforeach; ?>
- </select>
- </div>
-
- <div class="form-group">
- <label><?php echo $t['sku']; ?></label>
- <input type="text" name="sku" id="sku_input_field" value="<?php echo $edit_product ? htmlspecialchars($edit_product['sku']) : ''; ?>" placeholder="π.χ. 001, 002...">
- </div>
- </div>
- <div style="display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 20px;">
- <div class="form-group">
- <label><?php echo $t['price']; ?></label>
- <input type="number" step="0.01" name="price" value="<?php echo $edit_product ? htmlspecialchars($edit_product['price']) : ''; ?>" required>
- </div>
-
- <div class="form-group">
- <label><?php echo $t['stock']; ?></label>
- <input type="number" name="quantity" value="<?php echo $edit_product ? (int)$edit_product['quantity'] : 1; ?>" min="0" required>
- </div>
- <div class="form-group">
- <label><?php echo $t['status']; ?></label>
- <select name="status">
- <option value="available" <?php echo ($edit_product && $edit_product['status'] == 'available') ? 'selected' : ''; ?>><?php echo $t['status_avail']; ?></option>
- <option value="sold" <?php echo ($edit_product && $edit_product['status'] == 'sold') ? 'selected' : ''; ?>><?php echo $t['status_sold']; ?></option>
- <option value="hidden" <?php echo ($edit_product && $edit_product['status'] == 'hidden') ? 'selected' : ''; ?>><?php echo $t['status_hidden']; ?></option>
- </select>
- </div>
- <div class="form-group">
- <label><?php echo $t['seller_assign']; ?></label>
- <select name="seller_id" required>
- <?php foreach($all_users as $usr): ?>
- <option value="<?php echo $usr['id']; ?>" <?php echo ($edit_product && $edit_product['seller_id'] == $usr['id']) ? 'selected' : ((!$edit_product && $_SESSION['user_id'] == $usr['id']) ? 'selected' : ''); ?>>
- <?php echo htmlspecialchars($user_map[$usr['id']]); ?>
- </option>
- <?php endforeach; ?>
- </select>
- </div>
- </div>
-
- <div class="form-group">
- <label><?php echo $t['tags']; ?></label>
- <div class="tags-input-container" id="tags_container">
- <input type="text" id="tag_input_field" placeholder="<?php echo $t['tags_placeholder']; ?>">
- </div>
- <input type="hidden" name="search_tags" id="hidden_search_tags" value="<?php echo $edit_product ? htmlspecialchars($edit_product['search_tags']) : ''; ?>">
- <small style="color: var(--text-muted); font-size: 12px; margin-top:6px; display:block;"><?php echo $t['tags_hint']; ?></small>
- </div>
- <div id="dynamic-attributes-container" style="background: rgba(0,0,0,0.1); padding: 5px 20px; border-radius: 8px; border: 1px solid var(--border-color); margin-bottom: 20px; display: none;"></div>
- <div class="form-group">
- <label><?php echo $t['desc']; ?></label>
- <textarea name="description" rows="4"><?php echo $edit_product ? htmlspecialchars($edit_product['description']) : ''; ?></textarea>
- </div>
- <div class="form-group">
- <label><?php echo $t['photo']; ?> <span style="color: var(--text-muted); font-size: 11px; text-transform: none;"><?php echo $edit_product ? $t['photo_hint_edit'] : $t['photo_hint_add']; ?></span></label>
- <input type="file" name="image" accept="image/jpeg, image/png, image/gif" style="background: transparent; border: 1px dashed var(--border-color); padding: 15px;">
- </div>
-
- <div style="display: flex; gap: 10px; margin-top: 10px;">
- <button type="submit" class="btn"><?php echo $edit_product ? $t['btn_update'] : $t['btn_add']; ?></button>
- <?php if ($edit_product): ?>
- <a href="products.php" class="btn-cancel"><?php echo $t['btn_cancel']; ?></a>
- <?php endif; ?>
- </div>
- </form>
- </div>
- <div class="card">
- <h3><?php echo $t['list_title']; ?></h3>
- <table>
- <thead>
- <tr>
- <th><?php echo $t['th_id_sku']; ?></th>
- <th><?php echo $t['th_photo']; ?></th>
- <th><?php echo $t['th_title']; ?></th>
- <th><?php echo $t['th_cat']; ?></th>
- <th><?php echo $t['th_price']; ?></th>
- <th><?php echo $t['th_stock']; ?></th>
- <th><?php echo $t['th_status']; ?></th>
- <th><?php echo $t['th_actions']; ?></th>
- </tr>
- </thead>
- <tbody>
- <?php foreach($products as $prod): ?>
- <tr>
- <td>
- <strong>#<?php echo $prod['id']; ?></strong>
- <?php if(!empty($prod['sku'])): ?>
- <br><small style="color:var(--text-muted); font-weight:600;"><?php echo htmlspecialchars($prod['sku']); ?></small>
- <?php endif; ?>
- </td>
- <td>
- <?php if($prod['image_path']): ?>
- <img src="../<?php echo htmlspecialchars($prod['image_path']); ?>" class="thumb">
- <?php else: ?>
- <div style="width: 60px; height: 60px; background: rgba(0,0,0,0.2); border: 1px solid var(--border-color); display:flex; align-items:center; justify-content:center; border-radius:6px; font-size:10px; font-weight: bold; color: var(--text-muted);"><?php echo $t['no_img']; ?></div>
- <?php endif; ?>
- </td>
- <td>
- <strong style="font-size: 15px;"><?php echo htmlspecialchars($prod['title']); ?></strong>
- <?php if(!empty($prod['search_tags'])): ?>
- <div style="margin-top: 6px; display: flex; flex-wrap: wrap; gap: 4px;">
- <?php
- $tagsArr = explode(',', $prod['search_tags']);
- foreach($tagsArr as $tg) {
- echo '<span style="background:rgba(255,255,255,0.05); border:1px solid rgba(255,255,255,0.1); border-radius:12px; padding:2px 8px; font-size:10px; color: var(--text-muted); font-weight:600;">'.htmlspecialchars(trim($tg)).'</span>';
- }
- ?>
- </div>
- <?php endif; ?>
- </td>
- <td><?php echo htmlspecialchars($prod['cat_name']); ?></td>
- <td style="color: var(--primary); font-weight: 700;"><?php echo htmlspecialchars($prod['price']); ?> €</td>
- <td><?php echo (int)$prod['quantity']; ?> <?php echo $t['pcs']; ?></td>
- <td>
- <?php
- $status_class = 'badge-available';
- $status_label = $t['status_avail'];
- if ($prod['status'] == 'sold') { $status_class = 'badge-sold'; $status_label = $t['status_sold']; }
- if ($prod['status'] == 'hidden') { $status_class = 'badge-hidden'; $status_label = $t['status_hidden']; }
- ?>
- <span class="badge <?php echo $status_class; ?>">
- <?php echo $status_label; ?>
- </span>
- </td>
- <td>
- <div style="display: flex; gap: 6px;">
- <button type="button" class="action-btn"
- onclick="openModal(this)"
- data-product="<?php echo htmlspecialchars(json_encode($prod), ENT_QUOTES, 'UTF-8'); ?>">
- <?php echo $t['btn_view']; ?>
- </button>
- <a href="products.php?edit=<?php echo $prod['id']; ?>" class="action-btn btn-edit">Edit</a>
- <a href="products.php?delete=<?php echo $prod['id']; ?>" class="action-btn btn-delete" onclick="return confirm('<?php echo $t['confirm_del']; ?>');">Del</a>
- </div>
- </td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
- </div>
- </div>
- <!-- Modal Προβολής -->
- <div id="productModal" class="modal">
- <div class="modal-content">
- <span class="close-btn" onclick="closeModal()">×</span>
- <h2 id="modal-title" style="margin-top: 0; color: var(--text-main); font-size: 22px;"></h2>
-
- <div class="modal-body">
- <div style="flex: 1;">
- <img id="modal-img" src="" alt="Photo" style="display: none;">
- </div>
- <div class="modal-info" style="flex: 1.5;">
- <p><strong><?php echo $t['modal_id']; ?></strong> #<span id="modal-id"></span></p>
- <p><strong><?php echo $t['modal_sku']; ?></strong> <span id="modal-sku" style="color: var(--primary);"></span></p>
- <p><strong><?php echo $t['modal_cat']; ?></strong> <span id="modal-cat"></span></p>
- <p><strong><?php echo $t['modal_price']; ?></strong> <span id="modal-price" style="color: #10b981; font-weight: bold; font-size: 18px;"></span> €</p>
- <p><strong><?php echo $t['modal_stock']; ?></strong> <span id="modal-qty"></span> <?php echo $t['pcs']; ?></p>
- <p><strong><?php echo $t['modal_status']; ?></strong> <span id="modal-status" style="text-transform: uppercase; font-size: 12px; background: rgba(255,255,255,0.1); padding: 2px 6px; border-radius: 4px;"></span></p>
- <p><strong><?php echo $t['modal_seller']; ?></strong> <span id="modal-seller" style="color: var(--primary);"></span></p>
- <p><strong><?php echo $t['modal_tags']; ?></strong> <span id="modal-tags"></span></p>
-
- <ul id="modal-attributes" class="modal-attr-list"></ul>
- </div>
- </div>
-
- <h4 style="margin-bottom: 8px; color: var(--text-main); font-size: 16px; text-transform: uppercase;"><?php echo $t['modal_desc']; ?></h4>
- <div id="modal-desc" class="modal-desc"></div>
- </div>
- </div>
- <script>
- document.addEventListener('DOMContentLoaded', function() {
- const tagContainer = document.getElementById('tags_container');
- const tagInput = document.getElementById('tag_input_field');
- const hiddenTagsInput = document.getElementById('hidden_search_tags');
-
- let tags = [];
-
- if (hiddenTagsInput.value.trim() !== '') {
- tags = hiddenTagsInput.value.split(',').map(t => t.trim()).filter(t => t !== '');
- renderTags();
- }
- tagContainer.addEventListener('click', () => tagInput.focus());
- tagInput.addEventListener('keydown', function(e) {
- if (e.key === 'Enter' || e.key === ',') {
- e.preventDefault();
- addTag(tagInput.value);
- } else if (e.key === 'Backspace' && tagInput.value === '' && tags.length > 0) {
- removeTag(tags.length - 1);
- }
- });
- function addTag(tag) {
- tag = tag.replace(/,/g, '').trim();
- if (tag !== '' && !tags.includes(tag)) {
- tags.push(tag);
- updateHiddenAndRender();
- }
- tagInput.value = '';
- }
- window.removeTagEl = function(index) {
- tags.splice(index, 1);
- updateHiddenAndRender();
- };
- function removeTag(index) {
- window.removeTagEl(index);
- }
- function updateHiddenAndRender() {
- hiddenTagsInput.value = tags.join(', ');
- renderTags();
- }
- function renderTags() {
- tagContainer.querySelectorAll('.tag-chip').forEach(chip => chip.remove());
- tags.forEach((tag, index) => {
- const chip = document.createElement('div');
- chip.className = 'tag-chip';
- chip.innerHTML = `${tag} <span onclick="window.removeTagEl(${index})">×</span>`;
- tagContainer.insertBefore(chip, tagInput);
- });
- }
- });
- // ΝΕΑ ΣΥΝΑΡΤΗΣΗ: Υπολογισμός SKU
- function generateSku(categoryId) {
- const skuInput = document.getElementById('sku_input_field');
- if (!categoryId || skuInput.value.trim() !== '') return; // Παράγει μόνο αν το SKU είναι άδειο
-
- fetch('products.php?ajax_generate_sku=1&category_id=' + categoryId)
- .then(res => res.text())
- .then(sku => {
- if (sku.trim() !== '') {
- skuInput.value = sku.trim();
- }
- })
- .catch(err => console.error('SKU Fetch Error:', err));
- }
- function loadAttributes(categoryId, productId = 0) {
- const container = document.getElementById('dynamic-attributes-container');
- if (!categoryId) {
- container.style.display = 'none';
- container.innerHTML = '';
- return;
- }
-
- let url = 'products.php?ajax_get_attributes=1&category_id=' + categoryId;
- if (productId > 0) {
- url += '&product_id=' + productId;
- }
-
- fetch(url)
- .then(response => response.text())
- .then(html => {
- if (html.trim() !== '') {
- container.innerHTML = html;
- container.style.display = 'block';
- } else {
- container.style.display = 'none';
- container.innerHTML = '';
- }
- })
- .catch(error => console.error('Error:', error));
- }
- <?php if ($edit_product): ?>
- window.addEventListener('DOMContentLoaded', () => {
- loadAttributes(<?php echo $edit_product['category_id']; ?>, <?php echo $edit_product['id']; ?>);
- });
- <?php endif; ?>
- function openModal(button) {
- const product = JSON.parse(button.getAttribute('data-product'));
-
- document.getElementById('modal-id').innerText = product.id;
- document.getElementById('modal-sku').innerText = product.sku || '-';
- document.getElementById('modal-title').innerText = product.title;
- document.getElementById('modal-cat').innerText = product.cat_name;
- document.getElementById('modal-price').innerText = product.price;
- document.getElementById('modal-qty').innerText = product.quantity;
-
- let stLabel = '<?php echo $t['status_avail']; ?>';
- if (product.status === 'sold') stLabel = '<?php echo $t['status_sold']; ?>';
- if (product.status === 'hidden') stLabel = '<?php echo $t['status_hidden']; ?>';
- document.getElementById('modal-status').innerText = stLabel;
-
- document.getElementById('modal-seller').innerText = product.seller_display;
-
- document.getElementById('modal-desc').innerText = product.description || '-';
- document.getElementById('modal-tags').innerText = product.search_tags || '-';
-
- const attrList = document.getElementById('modal-attributes');
- attrList.innerHTML = '';
-
- if (product.attributes && product.attributes.length > 0) {
- product.attributes.forEach(attr => {
- let unit = attr.unit ? ' ' + attr.unit : '';
- let li = document.createElement('li');
- li.innerHTML = `<strong>${attr.display_name}:</strong> ${attr.value}${unit}`;
- attrList.appendChild(li);
- });
- } else {
- attrList.innerHTML = '<li><em><?php echo $t['modal_no_attrs']; ?></em></li>';
- }
-
- const imgEl = document.getElementById('modal-img');
- if (product.image_path) {
- imgEl.src = '../' + product.image_path;
- imgEl.style.display = 'block';
- } else {
- imgEl.style.display = 'none';
- }
-
- document.getElementById('productModal').style.display = 'block';
- }
- function closeModal() {
- document.getElementById('productModal').style.display = 'none';
- }
- window.onclick = function(event) {
- const modal = document.getElementById('productModal');
- if (event.target == modal) {
- closeModal();
- }
- }
- </script>
- </body>
- </html>
|