diff --git a/_export_sales_report.php b/_export_sales_report.php new file mode 100644 index 0000000..f59af43 --- /dev/null +++ b/_export_sales_report.php @@ -0,0 +1,71 @@ += ?"; + $params[] = $start_date . ' 00:00:00'; +} +if ($end_date) { + $conditions[] = "s.sale_date <= ?"; + $params[] = $end_date . ' 23:59:59'; +} +if ($payment_method) { + $conditions[] = "s.payment_method = ?"; + $params[] = $payment_method; +} + +if (count($conditions) > 0) { + $sql .= " WHERE " . implode(' AND ', $conditions); +} + +$sql .= " GROUP BY s.id ORDER BY s.sale_date DESC"; + +$stmt = $pdo->prepare($sql); +$stmt->execute($params); +$sales = $stmt->fetchAll(PDO::FETCH_ASSOC); + +// CSV generation +$filename = "sales_report_" . date('Y-m-d') . ".csv"; + +header('Content-Type: text/csv'); +header('Content-Disposition: attachment; filename="' . $filename . '"'); + +$output = fopen('php://output', 'w'); + +// Add BOM to support UTF-8 in Excel +fputs($output, "\xEF\xBB\xBF"); + +// Header row +fputcsv($output, ['Sale ID', 'Date', 'Total Amount', 'Payment Method', 'Items']); + +// Data rows +foreach ($sales as $sale) { + fputcsv($output, [ + $sale['id'], + $sale['sale_date'], + number_format($sale['total_amount'], 2), + $sale['payment_method'], + $sale['items'] + ]); +} + +fclose($output); +exit; diff --git a/_get_best_selling_products.php b/_get_best_selling_products.php new file mode 100644 index 0000000..359c17b --- /dev/null +++ b/_get_best_selling_products.php @@ -0,0 +1,27 @@ +query(" + SELECT + p.name, + SUM(si.quantity) as total_quantity + FROM sale_items si + JOIN products p ON si.product_id = p.id + GROUP BY p.name + ORDER BY total_quantity DESC + LIMIT 5 + "); + + $best_selling_products = $stmt->fetchAll(PDO::FETCH_ASSOC); + + header('Content-Type: application/json'); + echo json_encode($best_selling_products); + +} catch (PDOException $e) { + http_response_code(500); + echo json_encode(['error' => 'Database error: ' . $e->getMessage()]); +} +?> \ No newline at end of file diff --git a/_get_sale_items.php b/_get_sale_items.php new file mode 100644 index 0000000..2ec0245 --- /dev/null +++ b/_get_sale_items.php @@ -0,0 +1,22 @@ + 'Sale ID not provided.']); + exit; +} + +$sale_id = $_GET['sale_id']; +$pdo = db(); + +$stmt = $pdo->prepare(" + SELECT si.quantity, si.price, p.name as product_name + FROM sale_items si + JOIN products p ON si.product_id = p.id + WHERE si.sale_id = ? +"); +$stmt->execute([$sale_id]); +$items = $stmt->fetchAll(PDO::FETCH_ASSOC); + +echo json_encode($items); diff --git a/_get_sales_data.php b/_get_sales_data.php new file mode 100644 index 0000000..c69e682 --- /dev/null +++ b/_get_sales_data.php @@ -0,0 +1,42 @@ +prepare($sql); +$stmt->bindParam(':start_date', $p_start_date, PDO::PARAM_STR); +$stmt->bindParam(':end_date', $p_end_date, PDO::PARAM_STR); +$stmt->execute(); + +$sales_data = $stmt->fetchAll(PDO::FETCH_ASSOC); + +$labels = []; +$data = []; + +foreach ($sales_data as $row) { + $labels[] = date('M d', strtotime($row['sale_date'])); + $data[] = $row['daily_total']; +} + +echo json_encode(['labels' => $labels, 'data' => $data]); diff --git a/_handle_add_product.php b/_handle_add_product.php new file mode 100644 index 0000000..3cbee73 --- /dev/null +++ b/_handle_add_product.php @@ -0,0 +1,32 @@ +prepare("INSERT INTO products (name, sku, category, price, stock) VALUES (?, ?, ?, ?, ?)"); + $stmt->execute([$name, $sku, $category, $price, $stock]); + + // Redirect to product list on success + header('Location: products.php?success=Product+added'); + exit; + } catch (PDOException $e) { + // Handle error, e.g., duplicate SKU + // For now, we'll just redirect with a generic error + error_log($e->getMessage()); + header('Location: product_add.php?error=Could+not+add+product'); + exit; + } +} diff --git a/_handle_checkout.php b/_handle_checkout.php new file mode 100644 index 0000000..7e6f437 --- /dev/null +++ b/_handle_checkout.php @@ -0,0 +1,67 @@ + false, 'message' => 'An unknown error occurred.']; + +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + http_response_code(405); + $response['message'] = 'Invalid request method.'; + echo json_encode($response); + exit; +} + +$data = json_decode(file_get_contents('php://input'), true); +$cart = $data['cart'] ?? []; +$paymentMethod = $data['payment_method'] ?? 'Cash'; + +if (empty($cart)) { + http_response_code(400); + $response['message'] = 'Cart is empty.'; + echo json_encode($response); + exit; +} + +$pdo = db(); +try { + $pdo->beginTransaction(); + + $totalAmount = 0; + foreach ($cart as $item) { + $totalAmount += $item['price'] * $item['quantity']; + } + + $transactionId = 'TXN-' . strtoupper(uniqid()); + $stmt = $pdo->prepare("INSERT INTO sales (transaction_id, total_amount, payment_method) VALUES (?, ?, ?)"); + $stmt->execute([$transactionId, $totalAmount, $paymentMethod]); + $saleId = $pdo->lastInsertId(); + + $itemStmt = $pdo->prepare("INSERT INTO sale_items (sale_id, product_id, quantity, price) VALUES (?, ?, ?, ?)"); + $stockStmt = $pdo->prepare("UPDATE products SET stock = stock - ? WHERE id = ?"); + + foreach ($cart as $item) { + $productId = $item['id']; + $quantity = $item['quantity']; + $price = $item['price']; + + $itemStmt->execute([$saleId, $productId, $quantity, $price]); + $stockStmt->execute([$quantity, $productId]); + } + + $pdo->commit(); + + $response['success'] = true; + $response['message'] = 'Checkout successful!'; + $response['transaction_id'] = $transactionId; + http_response_code(200); + +} catch (Exception $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + http_response_code(500); + $response['message'] = 'Checkout failed: ' . $e->getMessage(); +} + +echo json_encode($response); diff --git a/_handle_delete_product.php b/_handle_delete_product.php new file mode 100644 index 0000000..90e3f0b --- /dev/null +++ b/_handle_delete_product.php @@ -0,0 +1,30 @@ +prepare('DELETE FROM products WHERE id = ?'); + $stmt->execute([$id]); + + // Check if any row was deleted + if ($stmt->rowCount() > 0) { + header('Location: products.php?status=deleted'); + } else { + header('Location: products.php?error=not_found'); + } + exit; +} catch (PDOException $e) { + // Handle DB error + // error_log($e->getMessage()); + header('Location: products.php?error=db'); + exit; +} diff --git a/_handle_edit_product.php b/_handle_edit_product.php new file mode 100644 index 0000000..7c0ac14 --- /dev/null +++ b/_handle_edit_product.php @@ -0,0 +1,39 @@ +prepare( + 'UPDATE products SET name = ?, sku = ?, category = ?, price = ?, stock = ? WHERE id = ?' + ); + $stmt->execute([$name, $sku, $category, $price, $stock, $id]); + + // Redirect to products list on success + header('Location: products.php?status=updated'); + exit; + } catch (PDOException $e) { + // Handle DB error, e.g., log error and redirect + // For development, you might want to see the error + // error_log($e->getMessage()); + header('Location: product_edit.php?id=' . $id . '&error=db'); + exit; + } +} + +// Redirect if accessed directly +header('Location: products.php'); +exit; diff --git a/_handle_login.php b/_handle_login.php new file mode 100644 index 0000000..0e34386 --- /dev/null +++ b/_handle_login.php @@ -0,0 +1,40 @@ + false, 'message' => 'Invalid username or password.']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $username = $_POST['username'] ?? ''; + $password = $_POST['password'] ?? ''; + + if (empty($username) || empty($password)) { + $response['message'] = 'Username and password are required.'; + echo json_encode($response); + exit; + } + + try { + $pdo = db(); + + $stmt = $pdo->prepare("SELECT id, username, password FROM users WHERE username = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(); + + if ($user && password_verify($password, $user['password'])) { + $_SESSION['user_id'] = $user['id']; + $_SESSION['username'] = $user['username']; + $response['success'] = true; + $response['message'] = 'Login successful.'; + } else { + $response['message'] = 'Invalid username or password.'; + } + } catch (PDOException $e) { + $response['message'] = 'Database error: ' . $e->getMessage(); + } + + echo json_encode($response); +} +?> \ No newline at end of file diff --git a/_handle_register.php b/_handle_register.php new file mode 100644 index 0000000..5ef740f --- /dev/null +++ b/_handle_register.php @@ -0,0 +1,48 @@ + false, 'message' => 'An error occurred.']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $username = $_POST['username'] ?? ''; + $password = $_POST['password'] ?? ''; + + if (empty($username) || empty($password)) { + $response['message'] = 'Username and password are required.'; + echo json_encode($response); + exit; + } + + try { + $pdo = db(); + + // Check if username already exists + $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?"); + $stmt->execute([$username]); + if ($stmt->fetch()) { + $response['message'] = 'Username already taken.'; + echo json_encode($response); + exit; + } + + // Hash the password + $password_hash = password_hash($password, PASSWORD_DEFAULT); + + // Insert the new user + $stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)"); + if ($stmt->execute([$username, $password_hash])) { + $response['success'] = true; + $response['message'] = 'Registration successful. You can now log in.'; + } else { + $response['message'] = 'Failed to register user.'; + } + } catch (PDOException $e) { + // In a real application, you would log this error. + $response['message'] = 'Database error: ' . $e->getMessage(); + } + + echo json_encode($response); +} +?> \ No newline at end of file diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..f0850e0 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,58 @@ +/* +SinarKasihMart Custom Styles +Primary: #0d6efd (Blue) +Secondary: #198754 (Green) +*/ + +body { + background-color: #f8f9fa; +} + +.navbar { + margin-bottom: 1.5rem; +} + +.card { + border-radius: 0.375rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); +} + +.btn-primary { + background-color: #0d6efd; + border-color: #0d6efd; +} + +.btn-success { + background-color: #198754; + border-color: #198754; +} + +.nav-link.active { + font-weight: 500; +} + +.product-card { + transition: transform .2s ease-in-out, box-shadow .2s ease-in-out; +} + +.product-card:hover { + transform: translateY(-5px); + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); +} + +#saleItemsModal .modal-body { + max-height: 400px; + overflow-y: auto; +} + +#salesChart { + max-height: 320px; +} + +#bestSellingChart { + max-height: 320px; +} + +.chart-container { + height: 400px; /* Adjust as needed */ +} diff --git a/catalog.php b/catalog.php new file mode 100644 index 0000000..f82a4fa --- /dev/null +++ b/catalog.php @@ -0,0 +1,41 @@ +query('SELECT * FROM products WHERE stock > 0 ORDER BY created_at DESC'); +$products = $stmt->fetchAll(); + +?> + +
No products are currently available.
+Rp
+