Add files via upload

This commit is contained in:
mochamadyunusikhsanodin 2025-07-11 14:27:54 +07:00 committed by GitHub
parent 644b6892b5
commit b9dd39854b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 17386 additions and 0 deletions

BIN
bootstrap.zip Normal file

Binary file not shown.

5
index.php Normal file
View File

@ -0,0 +1,5 @@
<?php
// Redirect to the views/index.php
header('Location: views/index.php');
exit();
?>

16
routes/db_conn.php Normal file
View File

@ -0,0 +1,16 @@
<?php
$servername = "localhost"; // your database server
$username = "root"; // your database username
$password = ""; // your database password
$dbname = "ayula_store"; // your database name
// Create connection
$conn = mysqli_connect($servername, $username, $password, $dbname);
// Check connection
if (!$conn) {
die("Koneksi gagal: " . mysqli_connect_error());
} else {
echo "";
}
?>

BIN
src/img/logoayula.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
src/img/smallest-ayula.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
src/img/userprofile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/img/warning.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

385
views/barang/addproduct.php Normal file
View File

@ -0,0 +1,385 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
// Ambil informasi user yang sedang login
$userRole = $_SESSION['role']; // 'user' atau 'admin'
$username = $_SESSION['username']; // Menambahkan username dari session
// Jika username adalah root, tampilkan nama yang lebih presentable
$displayName = ($username === 'root') ? 'Admin' : $username;
// Cek apakah session 'user_id' ada, yang berarti pengguna sudah login
if (!isset($_SESSION['user_id'])) {
// Jika session tidak ada, arahkan pengguna ke halaman login
header("Location: /ayula-store/index.php");
exit();
}
// Database connection
include('../../routes/db_conn.php');
// Function to generate new product code
function generateProductCode($conn) {
// Query the database for the highest product code
$query = "SELECT kode_barang FROM barang WHERE kode_barang LIKE 'BRG%' ORDER BY kode_barang DESC LIMIT 1";
$result = $conn->query($query);
if ($result->num_rows > 0) {
// Extract existing code
$row = $result->fetch_assoc();
$lastCode = $row['kode_barang'];
// Extract the numeric part and increment
$numericPart = intval(substr($lastCode, 3)); // Extract numbers after 'BRG'
$nextNumeric = $numericPart + 1;
// Format with leading zeros (e.g., BRG001, BRG002, etc.)
$newCode = 'BRG' . str_pad($nextNumeric, 3, '0', STR_PAD_LEFT);
} else {
// If no existing codes, start with BRG001
$newCode = 'BRG001';
}
return $newCode;
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Generate new product code
$kode_barang = generateProductCode($conn);
// Get data from form
$nama_barang = isset($_POST['nama_barang']) ? $_POST['nama_barang'] : '';
$id_jenis = isset($_POST['id_jenis']) ? $_POST['id_jenis'] : '';
$stok = isset($_POST['stok']) ? $_POST['stok'] : 0;
$harga = isset($_POST['harga']) ? $_POST['harga'] : '';
// Check if image file was uploaded
if (isset($_FILES['image']) && $_FILES['image']['error'] == 0) {
$image = $_FILES['image'];
// Validate image
$image_name = basename($image['name']);
$target_dir = "../uploads/img-barang/"; // Target folder to store images
$target_file = $target_dir . $image_name;
$image_file_type = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));
// Check if uploaded file is an image
if (in_array($image_file_type, ['jpg', 'jpeg', 'png', 'gif'])) {
// Move file to target folder
if (!move_uploaded_file($image['tmp_name'], $target_file)) {
echo "Error: Failed to upload image!";
exit;
}
} else {
echo "Error: Only images (JPG, JPEG, PNG, GIF) are allowed!";
exit;
}
} else {
$image_name = ''; // If no image was uploaded, set image name to empty
}
// Validate id_jenis
$query_jenis = "SELECT id_jenis FROM jenis_barang WHERE id_jenis = ?";
$stmt_jenis = $conn->prepare($query_jenis);
$stmt_jenis->bind_param("i", $id_jenis);
$stmt_jenis->execute();
$stmt_jenis->store_result();
if ($stmt_jenis->num_rows == 0 && $id_jenis != '') {
echo "Error: Category not found!";
exit;
}
// Validate stock and price must be numbers
if (!preg_match('/^\d+$/', $stok) || !preg_match('/^\d+$/', $harga)) {
die("<script>alert('Stock and price must contain only numbers!'); window.history.back();</script>");
}
// Convert to integers for safety
$stok = intval($stok);
$harga = intval($harga);
// Insert data into database (now including kode_barang)
$sql = "INSERT INTO barang (kode_barang, nama_barang, id_jenis, stok, harga, image)
VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ssisis", $kode_barang, $nama_barang, $id_jenis, $stok, $harga, $image_name);
if ($stmt->execute()) {
// Redirect with success parameter
header("Location: productlist.php?success_add=1");
exit();
}}
// Fetch categories for the dropdown
$query_categories = "SELECT id_jenis, nama_jenis FROM jenis_barang ORDER BY nama_jenis";
$result_categories = $conn->query($query_categories);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="description" content="POS - Bootstrap Admin Template">
<meta name="keywords"
content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects">
<meta name="author" content="Dreamguys - Bootstrap Admin Template">
<meta name="robots" content="noindex, nofollow">
<title>Dreams Pos admin template</title>
<link rel="shortcut icon" type="image/x-icon" href="/ayula-store/bootstrap/assets/img/favicon.jpg">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/style.css">
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"></div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($displayName); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
aria-expanded="false"><i class="fa fa-ellipsis-v"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/reporttt/report.php"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Barang</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php" >Daftar Barang</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php" class="active">Tambah Barang</a></li>
</ul>
</li>
<li >
<a href="/ayula-store/views/barang/topsis_restock_view.php"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
Analisa Barang</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Tambah Barang</h4>
</div>
</div>
<form action="addproduct.php" method="POST" enctype="multipart/form-data">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Nama Barang</label>
<input type="text" name="nama_barang" required>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Kategori</label>
<select class="select" name="id_jenis" required>
<option value="">Pilih Kategori</option>
<?php
// Loop through all categories and create option tags
if ($result_categories && $result_categories->num_rows > 0) {
while($category = $result_categories->fetch_assoc()) {
echo '<option value="' . $category['id_jenis'] . '">' . $category['nama_jenis'] . '</option>';
}
}
?>
</select>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Harga</label>
<input type="text" name="harga" required>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Stok</label>
<input type="text" name="stok" required>
</div>
</div>
<div class="col-lg-12">
<div class="form-group">
<label> Gambar Barang</label>
<div class="image-upload">
<input type="file" name="image" id="image" accept="image/*">
<div class="image-uploads">
<img src="/ayula-store/bootstrap/assets/img/icons/upload.svg" alt="img">
<h4>Pilih Gambar</h4>
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<button type="submit" class="btn btn-submit">Tambah</button>
<a href="productlist.php" class="btn btn-cancel">Batal</a>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<script src="/ayula-store/bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/feather.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/sweetalert/sweetalert2.all.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/sweetalert/sweetalerts.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/script.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
document.addEventListener("DOMContentLoaded", function () {
function onlyNumbers(event) {
let charCode = event.which ? event.which : event.keyCode;
if (charCode < 48 || charCode > 57) {
event.preventDefault();
Swal.fire({
icon: "error",
title: "Input Tidak Valid!",
text: "Hanya angka yang diperbolehkan.",
showConfirmButton: false,
timer: 2000
});
}
}
let stokInput = document.querySelector("input[name='stok']");
let hargaInput = document.querySelector("input[name='harga']");
stokInput.addEventListener("keypress", onlyNumbers);
hargaInput.addEventListener("keypress", onlyNumbers);
function validateOnBlur(input) {
input.addEventListener("blur", function () {
if (!/^\d+$/.test(input.value)) {
Swal.fire({
icon: "warning",
title: "Input Tidak Valid!",
text: "Harap masukkan angka saja.",
showConfirmButton: false,
timer: 2000
});
input.value = "";
}
});
}
validateOnBlur(stokInput);
validateOnBlur(hargaInput);
});
</script>
</body>
</html>

View File

@ -0,0 +1,49 @@
<?php
// This script creates the necessary directory structure for storing uploaded receipt images
// Define the directory path
$upload_dir = '../uploads/nota/';
// Check if directory exists
if (!file_exists($upload_dir)) {
// Create directory with full permissions (for development)
if (mkdir($upload_dir, 0777, true)) {
echo "Directory created successfully: " . $upload_dir;
// Set proper permissions (more secure for production)
chmod($upload_dir, 0755);
echo "<br>Permissions set to 0755";
// Create .htaccess file to protect direct access to images (optional)
$htaccess_content = "# Deny direct access to files
<FilesMatch \"\\.(jpg|jpeg|png|gif)$\">
Order Allow,Deny
Deny from all
</FilesMatch>
# Allow access through PHP scripts
<FilesMatch \"get_image\\.php$\">
Order Allow,Deny
Allow from all
</FilesMatch>";
file_put_contents($upload_dir . '/.htaccess', $htaccess_content);
echo "<br>.htaccess file created for security";
} else {
echo "Failed to create directory: " . $upload_dir;
echo "<br>Please create this directory manually and ensure it has write permissions.";
}
} else {
echo "Directory already exists: " . $upload_dir;
// Check if directory is writable
if (is_writable($upload_dir)) {
echo "<br>Directory is writable.";
} else {
echo "<br>Warning: Directory is not writable. Please set proper permissions.";
}
}
echo "<br><br><a href='/ayula-store/views/barang/productlist.php'>Return to Product List</a>";
?>

View File

@ -0,0 +1,404 @@
<?php
// Include database connection
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
// Ambil informasi user yang sedang login
$userRole = $_SESSION['role']; // 'user' atau 'admin'
$username = $_SESSION['username']; // Menambahkan username dari session
// Jika username adalah root, tampilkan nama yang lebih presentable
$displayName = ($username === 'root') ? 'Admin' : $username;
// Cek apakah session 'user_id' ada, yang berarti pengguna sudah login
if (!isset($_SESSION['user_id'])) {
// Jika session tidak ada, arahkan pengguna ke halaman login
header("Location: /ayula-store/index.php");
exit();
}
function dbConnect() {
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "ayula_store";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
return $conn;
}
// Get product by ID
function getProductById($id) {
$conn = dbConnect();
$sql = "SELECT * FROM barang WHERE id_barang = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
$product = $result->fetch_assoc();
$stmt->close();
$conn->close();
return $product;
}
// Get all product categories
function getProductCategories() {
$conn = dbConnect();
$sql = "SELECT * FROM jenis_barang ORDER BY nama_jenis";
$result = $conn->query($sql);
$categories = [];
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$categories[] = $row;
}
}
$conn->close();
return $categories;
}
//fungsi update product
function updateProduct($id, $data) {
$conn = dbConnect();
$sql = "UPDATE barang SET
nama_barang = ?,
harga = ?,
stok = ?,
id_jenis = ?,
image = ?
WHERE id_barang = ?";
$stmt = $conn->prepare($sql);
// Change from "ssdiis" to "ssdisi" - notice the 'i' to 's' change for image parameter
$stmt->bind_param("ssdisi",
$data['nama_barang'],
$data['harga'],
$data['stok'],
$data['id_jenis'],
$data['image'],
$id
);
$result = $stmt->execute();
$stmt->close();
$conn->close();
return $result;
}
// Check if form is submitted
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$id = $_POST['id_barang'];
// Handle image upload if a new file is selected
$image = $_POST['current_image']; // Default to current image
if(isset($_FILES['product_image']) && $_FILES['product_image']['size'] > 0) {
$target_dir = "../uploads/img-barang/";
$file_extension = strtolower(pathinfo($_FILES["product_image"]["name"], PATHINFO_EXTENSION));
$new_filename = "product_" . time() . "." . $file_extension;
$target_file = $target_dir . $new_filename;
// Check if image file is an actual image
$check = getimagesize($_FILES["product_image"]["tmp_name"]);
if($check !== false) {
// Upload file
if (move_uploaded_file($_FILES["product_image"]["tmp_name"], $target_file)) {
$image = $new_filename;
}
}
}
// Prepare data for update
$data = [
'nama_barang' => $_POST['nama_barang'],
'harga' => $_POST['harga'],
'stok' => $_POST['stok'],
'id_jenis' => $_POST['id_jenis'],
'image' => $image
];
// Update product
if(updateProduct($id, $data)) {
// Redirect to product list with success message
// After successfully editing a product
header("Location: productlist.php?success_edit=1");
exit();
} else {
$error = "Failed to update product";
}
}
// Get product ID from URL
$product_id = isset($_GET['id']) ? $_GET['id'] : 0;
$product = getProductById($product_id);
// Get all categories
$categories = getProductCategories();
// If product doesn't exist, redirect to product list
if (!$product) {
header("Location: productlist.php?error=Product not found");
exit();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="description" content="POS - Bootstrap Admin Template">
<meta name="keywords"
content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects">
<meta name="author" content="Dreamguys - Bootstrap Admin Template">
<meta name="robots" content="noindex, nofollow">
<title>Ayula Store - Edit Product</title>
<link rel="shortcut icon" type="image/x-icon" href="/ayula-store/bootstrap/assets/img/favicon.jpg">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/style.css">
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"></div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($displayName); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
aria-expanded="false"><i class="fa fa-ellipsis-v"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/reporttt/report.php"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li class="submenu" >
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Barang</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php" class="active">Daftar Barang</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Barang</a></li>
</ul>
</li>
<li >
<a href="/ayula-store/views/barang/topsis_restock_view.php"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
Analisa Barang</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Edit Barang</h4>
</div>
</div>
<div class="card">
<div class="card-body">
<form action="editproduct.php" method="POST" enctype="multipart/form-data">
<!-- Hidden field for product ID -->
<input type="hidden" name="id_barang" value="<?php echo $product['id_barang']; ?>">
<input type="hidden" name="current_image" value="<?php echo $product['image']; ?>">
<div class="row">
<div class="col-lg-4 col-sm-6 col-12">
<div class="form-group">
<label>Nama Barang</label>
<input type="text" name="nama_barang" value="<?php echo $product['nama_barang']; ?>" required>
</div>
</div>
<div class="col-lg-4 col-sm-6 col-12">
<div class="form-group">
<label>Kategori</label>
<select class="select" name="id_jenis" required>
<option value="">Pilih Kategori</option>
<?php foreach($categories as $category): ?>
<option value="<?php echo $category['id_jenis']; ?>" <?php if($category['id_jenis'] == $product['id_jenis']) echo 'selected'; ?>>
<?php echo $category['nama_jenis']; ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Stok</label>
<input type="text" name="stok" value="<?php echo $product['stok']; ?>" required>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Harga</label>
<input type="text" step="0.01" name="harga" value="<?php echo $product['harga']; ?>" required>
</div>
</div>
<div class="col-lg-12">
<div class="form-group">
<label>Gambar barang</label>
<div class="image-upload">
<input type="file" name="product_image" accept="image/*">
<div class="image-uploads">
<img src="/ayula-store/bootstrap/assets/img/icons/upload.svg" alt="img">
<h4>Pilih Gambar</h4>
</div>
</div>
</div>
</div>
<?php if($product['image']): ?>
<div class="col-12">
<div class="product-list">
<ul class="row">
<li>
<div class="productviews">
<div class="productviewsimg">
<img src="../uploads/img-barang/<?php echo $product['image']; ?>" alt="img">
</div>
<div class="productviewscontent">
<div class="productviewsname">
<h2><?php echo $product['image']; ?></h2>
</div>
</div>
</div>
</li>
</ul>
</div>
</div>
<?php endif; ?>
<div class="col-lg-12">
<button type="submit" class="btn btn-submit me-2">Edit</button>
<a href="productlist.php" class="btn btn-cancel">Batal</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="/ayula-store/bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/feather.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/sweetalert/sweetalert2.all.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/sweetalert/sweetalerts.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/script.js"></script>
</body>
</html>

View File

@ -0,0 +1,98 @@
<?php
// Koneksi ke database
$servername = "localhost";
$username = "root"; // Sesuaikan dengan username database kamu
$password = ""; // Sesuaikan dengan password database kamu
$database = "ayula_store"; // Sesuaikan dengan nama database kamu
$conn = new mysqli($servername, $username, $password, $database);
// Periksa koneksi
if ($conn->connect_error) {
die("Koneksi gagal: " . $conn->connect_error);
}
// Ambil data untuk laporan
$sql = "SELECT r.id_report, r.tanggal, b.nama_barang, jb.nama_jenis, r.jumlah, r.harga
FROM report r
JOIN barang b ON r.id_barang = b.id_barang
JOIN jenis_barang jb ON b.id_jenis = jb.id_jenis
ORDER BY r.tanggal DESC";
$result = $conn->query($sql);
// Set header for Excel file
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment; filename="laporan_barang_' . date('Y-m-d') . '.xls"');
header('Pragma: no-cache');
header('Expires: 0');
// Output tabel HTML yang akan dibaca sebagai Excel
echo '<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Laporan Barang</title>
<style>
table {border-collapse: collapse;}
th, td {border: 1px solid #000; padding: 5px;}
th {background-color: #f0f0f0; font-weight: bold;}
.text-right {text-align: right;}
.text-center {text-align: center;}
</style>
</head>
<body>
<h2>Laporan Barang - Ayula Store</h2>
<p>Tanggal Export: ' . date('d/m/Y H:i:s') . '</p>
<table>
<thead>
<tr>
<th>No</th>
<th>ID Laporan</th>
<th>Tanggal</th>
<th>Nama Barang</th>
<th>Jenis</th>
<th>Jumlah</th>
<th>Harga (Rp)</th>
<th>Total (Rp)</th>
</tr>
</thead>
<tbody>';
$no = 1;
$grand_total = 0;
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$total = $row['jumlah'] * $row['harga'];
$grand_total += $total;
echo '<tr>
<td class="text-center">' . $no++ . '</td>
<td>' . $row['id_report'] . '</td>
<td>' . date('d/m/Y H:i', strtotime($row['tanggal'])) . '</td>
<td>' . $row['nama_barang'] . '</td>
<td>' . $row['nama_jenis'] . '</td>
<td class="text-center">' . $row['jumlah'] . '</td>
<td class="text-right">' . number_format($row['harga'], 0, ',', '.') . '</td>
<td class="text-right">' . number_format($total, 0, ',', '.') . '</td>
</tr>';
}
echo '<tr>
<td colspan="7" class="text-right"><strong>Grand Total</strong></td>
<td class="text-right"><strong>' . number_format($grand_total, 0, ',', '.') . '</strong></td>
</tr>';
} else {
echo '<tr><td colspan="8" class="text-center">Tidak ada data laporan</td></tr>';
}
echo '</tbody>
</table>
</body>
</html>';
// Close database connection
$conn->close();
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

BIN
views/barang/image/mmm.JPEG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

View File

@ -0,0 +1,248 @@
<?php
// Koneksi ke database
$servername = "localhost";
$username = "root"; // Sesuaikan dengan username database kamu
$password = ""; // Sesuaikan dengan password database kamu
$database = "ayula_store"; // Sesuaikan dengan nama database kamu
$conn = new mysqli($servername, $username, $password, $database);
// Periksa koneksi
if ($conn->connect_error) {
die("Koneksi gagal: " . $conn->connect_error);
}
// Cek ID laporan
if (!isset($_GET['id']) || empty($_GET['id'])) {
die("ID Laporan tidak valid");
}
$id_report = $_GET['id'];
// Query untuk mengambil data laporan
$sql = "SELECT r.id_report, r.tanggal, b.id_barang, b.nama_barang, jb.nama_jenis,
r.jumlah, r.harga, r.image
FROM report r
JOIN barang b ON r.id_barang = b.id_barang
JOIN jenis_barang jb ON b.id_jenis = jb.id_jenis
WHERE r.id_report = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id_report);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 0) {
die("Laporan tidak ditemukan");
}
$report = $result->fetch_assoc();
$total = $report['jumlah'] * $report['harga'];
$image_url = !empty($report['image']) ? '../uploads/nota/' . $report['image'] : '/ayula-store/bootstrap/assets/img/no-image.png';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<title>Cetak Laporan - <?php echo $report['id_report']; ?></title>
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap.min.css" />
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
.invoice-header {
text-align: center;
margin-bottom: 30px;
}
.invoice-title {
font-size: 24px;
font-weight: bold;
margin-bottom: 5px;
}
.invoice-subtitle {
font-size: 16px;
color: #555;
margin-bottom: 15px;
}
.company-details {
margin-bottom: 20px;
}
.invoice-meta {
margin-bottom: 30px;
}
.invoice-meta table {
width: 100%;
}
.invoice-meta td {
padding: 5px;
}
.invoice-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 30px;
}
.invoice-table th, .invoice-table td {
border: 1px solid #ddd;
padding: 10px;
}
.invoice-table th {
background-color: #f5f5f5;
}
.text-right {
text-align: right;
}
.text-center {
text-align: center;
}
.receipt-image {
max-width: 100%;
max-height: 300px;
margin: 20px 0;
border: 1px solid #ddd;
}
.receipt-container {
text-align: center;
margin: 20px 0;
}
.signature-section {
margin-top: 50px;
display: flex;
justify-content: space-between;
}
.signature-box {
width: 45%;
text-align: center;
}
.signature-line {
border-top: 1px solid #000;
margin-top: 70px;
margin-bottom: 10px;
}
@media print {
body {
padding: 0;
margin: 0;
}
.no-print {
display: none;
}
@page {
margin: 15mm;
}
}
</style>
</head>
<body>
<div class="container">
<div class="no-print" style="text-align: right; margin-bottom: 15px;">
<button class="btn btn-primary" onclick="window.print()">Cetak Laporan</button>
<button class="btn btn-secondary" onclick="window.close()">Tutup</button>
</div>
<div class="invoice-header">
<div class="invoice-title">LAPORAN BARANG</div>
<div class="invoice-subtitle">AYULA STORE</div>
</div>
<div class="company-details">
<strong>Ayula Store</strong><br>
Jl. Contoh No. 123, Kota<br>
Telp: (021) 123-4567<br>
Email: info@ayulastore.com
</div>
<div class="invoice-meta">
<table>
<tr>
<td width="150"><strong>ID Laporan</strong></td>
<td>: <?php echo $report['id_report']; ?></td>
<td width="150"><strong>Tanggal</strong></td>
<td>: <?php echo date('d/m/Y H:i', strtotime($report['tanggal'])); ?></td>
</tr>
<tr>
<td><strong>ID Barang</strong></td>
<td>: <?php echo $report['id_barang']; ?></td>
<td><strong>Jenis Barang</strong></td>
<td>: <?php echo $report['nama_jenis']; ?></td>
</tr>
</table>
</div>
<table class="invoice-table">
<thead>
<tr>
<th width="5%">No</th>
<th width="45%">Nama Barang</th>
<th width="15%">Jumlah</th>
<th width="15%">Harga</th>
<th width="20%">Total</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-center">1</td>
<td><?php echo $report['nama_barang']; ?></td>
<td class="text-center"><?php echo $report['jumlah']; ?></td>
<td class="text-right">Rp <?php echo number_format($report['harga'], 0, ',', '.'); ?></td>
<td class="text-right">Rp <?php echo number_format($total, 0, ',', '.'); ?></td>
</tr>
<tr>
<td colspan="4" class="text-right"><strong>Total</strong></td>
<td class="text-right"><strong>Rp <?php echo number_format($total, 0, ',', '.'); ?></strong></td>
</tr>
</tbody>
</table>
<div class="receipt-container">
<h4>Gambar Nota</h4>
<img src="<?php echo $image_url; ?>" alt="Nota" class="receipt-image">
</div>
<div class="signature-section">
<div class="signature-box">
<div class="signature-line"></div>
<strong>Penanggung Jawab</strong>
</div>
<div class="signature-box">
<div class="signature-line"></div>
<strong>Admin</strong>
</div>
</div>
</div>
<script src="/ayula-store/bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script>
// Auto print when page loads
window.onload = function() {
// Add a small delay to ensure everything is loaded
setTimeout(function() {
//window.print();
}, 500);
};
</script>
</body>
</html>

View File

@ -0,0 +1,349 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
// Ambil informasi user yang sedang login
$userRole = $_SESSION['role']; // 'user' atau 'admin'
$username = $_SESSION['username']; // Menambahkan username dari session
// Jika username adalah root, tampilkan nama yang lebih presentable
$displayName = ($username === 'root') ? 'Admin' : $username;
// Cek apakah session 'user_id' ada, yang berarti pengguna sudah login
if (!isset($_SESSION['user_id'])) {
// Jika session tidak ada, arahkan pengguna ke halaman login
header("Location: /ayula-store/index.php");
exit();
}
// Database connection
include('../../routes/db_conn.php');
$product_id = $_GET['id']; // Fetching product ID from URL parameter
// SQL query to fetch product details
$sql = "SELECT * FROM barang WHERE id_barang = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param('i', $product_id); // Bind product_id as integer
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$product = $result->fetch_assoc();
} else {
echo "Product not found.";
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="description" content="POS - Bootstrap Admin Template">
<meta name="keywords"
content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects">
<meta name="author" content="Dreamguys - Bootstrap Admin Template">
<meta name="robots" content="noindex, nofollow">
<title>Dreams Pos admin template</title>
<link rel="shortcut icon" type="image/x-icon" href="/ayula-store/bootstrap/assets/img/favicon.jpg">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/owlcarousel/owl.carousel.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/style.css">
<!-- Add JsBarcode library -->
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"></script>
<style>
.barcode-container {
text-align: center;
margin-bottom: 20px;
}
.barcode-label {
display: block;
font-size: 14px;
margin-top: 5px;
font-weight: bold;
}
.barcode-print-btn {
margin-top: 10px;
display: inline-block;
background-color: #ff9f43;
color: white;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
}
.barcode-print-btn:hover {
background-color: #ff8a1e;
color: white;
}
.barcode-print-btn i {
margin-right: 5px;
}
@media print {
body * {
visibility: hidden;
}
.barcode-print-section, .barcode-print-section * {
visibility: visible;
}
.barcode-print-section {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
.barcode-print-btn {
display: none;
}
}
</style>
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"></div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($displayName); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
aria-expanded="false"><i class="fa fa-ellipsis-v"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/reporttt/report.php"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Barang</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php" class="active">Daftar Barang</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Barang</a></li>
</ul>
</li>
<li >
<a href="/ayula-store/views/barang/topsis_restock_view.php"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
Analisa Barang</span>
</a>
</li>
<!-- Other menu items -->
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Detail barang</h4>
<h6></h6>
</div>
</div>
<div class="row">
<div class="col-lg-8 col-sm-12">
<div class="card">
<div class="card-body">
<!-- Barcode Section -->
<div class="barcode-container barcode-print-section">
<svg id="barcode"></svg>
<span class="barcode-label"><?php echo isset($product['kode_barang']) ? $product['kode_barang'] : 'No Product Code'; ?></span>
<a href="javascript:void(0);" class="barcode-print-btn" onclick="printBarcode()">
<i class="fa fa-print"></i> Print Barcode
</a>
</div>
<div class="productdetails">
<ul class="product-bar">
<li>
<h4>barang</h4>
<h6><?php echo isset($product['nama_barang']) ? $product['nama_barang'] : ''; ?></h6>
</li>
<li>
<h4>kode barang</h4>
<h6><?php echo isset($product['kode_barang']) ? $product['kode_barang'] : ''; ?></h6>
</li>
<li>
<h4>Stok</h4>
<h6><?php echo isset($product['stok']) ? $product['stok'] : '0'; ?></h6>
</li>
<li>
<h4>Harga</h4>
<h6>Rp <?php echo isset($product['harga']) ? number_format($product['harga'], 0, ',', '.') : '0'; ?></h6>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="col-lg-4 col-sm-12">
<div class="card">
<div class="card-body">
<div class="slider-product-details">
<div class="owl-carousel owl-theme product-slide">
<div class="slider-product">
<?php if (isset($product['image']) && !empty($product['image'])): ?>
<img src="../uploads/img-barang/<?php echo $product['image']; ?>" alt="Product Image">
<h4><?php echo $product['image']; ?></h4>
<?php else: ?>
<img src="/ayula-store/bootstrap/assets/img/product/noimage.png" alt="No Image">
<h4>No Image Available</h4>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="/ayula-store/bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/feather.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/owlcarousel/owl.carousel.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/script.js"></script>
<script>
// Generate barcode
$(document).ready(function() {
<?php if (isset($product['kode_barang']) && !empty($product['kode_barang'])): ?>
JsBarcode("#barcode", "<?php echo $product['kode_barang']; ?>", {
format: "CODE128",
width: 2,
height: 70,
displayValue: false,
margin: 10,
background: "#ffffff",
lineColor: "#000000"
});
<?php else: ?>
JsBarcode("#barcode", "BRG000", {
format: "CODE128",
width: 2,
height: 70,
displayValue: false,
margin: 10,
background: "#ffffff",
lineColor: "#000000"
});
<?php endif; ?>
});
// Function to print barcode
function printBarcode() {
window.print();
}
</script>
</body>
</html>

1284
views/barang/productlist.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
<?php
// Koneksi ke database
$servername = "localhost";
$username = "root";
$password = "";
$database = "ayula_store";
$conn = new mysqli($servername, $username, $password, $database);
// Periksa koneksi
if ($conn->connect_error) {
die(json_encode(['success' => false, 'message' => 'Koneksi database gagal: ' . $conn->connect_error]));
}
// Cek action yang diminta
if (isset($_POST['action']) && $_POST['action'] == 'create_report') {
createReport($conn);
} else {
echo json_encode(['success' => false, 'message' => 'Aksi tidak valid']);
}
function createReport($conn) {
// Handle upload gambar nota
$upload_dir = '../uploads/nota/';
// Buat direktori jika belum ada
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0777, true);
}
$image_name = '';
if (isset($_FILES['receipt_image']) && $_FILES['receipt_image']['error'] == 0) {
// Generate nama file unik
$file_extension = pathinfo($_FILES['receipt_image']['name'], PATHINFO_EXTENSION);
$image_name = 'nota_' . time() . '_' . mt_rand(1000, 9999) . '.' . $file_extension;
$target_file = $upload_dir . $image_name;
// Validasi tipe file
$allowed_types = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array(strtolower($file_extension), $allowed_types)) {
echo json_encode(['success' => false, 'message' => 'Format file tidak didukung. Gunakan JPG, JPEG, PNG, atau GIF']);
return;
}
// Validasi ukuran file (max 5MB)
if ($_FILES['receipt_image']['size'] > 5 * 1024 * 1024) {
echo json_encode(['success' => false, 'message' => 'Ukuran file terlalu besar (maksimum 5MB)']);
return;
}
// Upload file
if (!move_uploaded_file($_FILES['receipt_image']['tmp_name'], $target_file)) {
echo json_encode(['success' => false, 'message' => 'Gagal mengunggah gambar']);
return;
}
} else {
echo json_encode(['success' => false, 'message' => 'Gambar nota harus diunggah']);
return;
}
// Mulai transaksi
$conn->begin_transaction();
try {
// Loop through the items to insert
if (isset($_POST['id_barang'])) {
foreach ($_POST['id_barang'] as $key => $id_barang) {
$jumlah = $_POST['jumlah'][$key] ?? '';
$harga = $_POST['harga'][$key] ?? '';
// Validasi data
if (empty($id_barang) || empty($jumlah) || empty($harga)) {
echo json_encode(['success' => false, 'message' => 'Semua field harus diisi']);
return;
}
// Buat ID report otomatis (format: RPT-YYYYMMDDxxx)
$date = date('Ymd');
$query = "SELECT MAX(SUBSTRING(id_report, 12)) as last_id FROM report WHERE id_report LIKE 'RPT-$date%'";
$result = $conn->query($query);
$row = $result->fetch_assoc();
$last_id = $row['last_id'] ?? 0;
$new_id = 'RPT-' . $date . str_pad(intval($last_id) + 1, 3, '0', STR_PAD_LEFT);
// Simpan data ke tabel report
$query = "INSERT INTO report (id_report, id_barang, tanggal, jumlah, harga, image)
VALUES (?, ?, NOW(), ?, ?, ?)";
$stmt = $conn->prepare($query);
$stmt->bind_param('sssss', $new_id, $id_barang, $jumlah, $harga, $image_name);
$stmt->execute();
if ($stmt->affected_rows <= 0) {
throw new Exception("Gagal menyimpan data report");
}
}
} else {
throw new Exception("ID Barang tidak ditemukan");
}
// Commit transaksi
$conn->commit();
echo json_encode([
'success' => true,
'message' => 'Data berhasil ditambahkan ke laporan',
'image_url' => '/ayula-store/uploads/nota/' . $image_name
]);
} catch (Exception $e) {
// Rollback transaksi jika terjadi kesalahan
$conn->rollback();
// Hapus file gambar jika upload sudah terjadi
if (!empty($image_name) && file_exists($upload_dir . $image_name)) {
unlink($upload_dir . $image_name);
}
echo json_encode(['success' => false, 'message' => 'Error: ' . $e->getMessage()]);
}
}
?>

View File

@ -0,0 +1,98 @@
<?php
// Koneksi ke database
$servername = "localhost";
$username = "root"; // Sesuaikan dengan username database kamu
$password = ""; // Sesuaikan dengan password database kamu
$database = "ayula_store"; // Sesuaikan dengan nama database kamu
$conn = new mysqli($servername, $username, $password, $database);
// Periksa koneksi
if ($conn->connect_error) {
die("Koneksi gagal: " . $conn->connect_error);
}
// Ambil data untuk laporan
$sql = "SELECT r.id_report, r.tanggal, b.nama_barang, jb.nama_jenis, r.jumlah, r.harga
FROM report r
JOIN barang b ON r.id_barang = b.id_barang
JOIN jenis_barang jb ON b.id_jenis = jb.id_jenis
ORDER BY r.tanggal DESC";
$result = $conn->query($sql);
// Set header for Excel file
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment; filename="laporan_barang_' . date('Y-m-d') . '.xls"');
header('Pragma: no-cache');
header('Expires: 0');
// Output tabel HTML yang akan dibaca sebagai Excel
echo '<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Laporan Barang</title>
<style>
table {border-collapse: collapse;}
th, td {border: 1px solid #000; padding: 5px;}
th {background-color: #f0f0f0; font-weight: bold;}
.text-right {text-align: right;}
.text-center {text-align: center;}
</style>
</head>
<body>
<h2>Laporan Barang - Ayula Store</h2>
<p>Tanggal Export: ' . date('d/m/Y H:i:s') . '</p>
<table>
<thead>
<tr>
<th>No</th>
<th>ID Laporan</th>
<th>Tanggal</th>
<th>Nama Barang</th>
<th>Jenis</th>
<th>Jumlah</th>
<th>Harga (Rp)</th>
<th>Total (Rp)</th>
</tr>
</thead>
<tbody>';
$no = 1;
$grand_total = 0;
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
$total = $row['jumlah'] * $row['harga'];
$grand_total += $total;
echo '<tr>
<td class="text-center">' . $no++ . '</td>
<td>' . $row['id_report'] . '</td>
<td>' . date('d/m/Y H:i', strtotime($row['tanggal'])) . '</td>
<td>' . $row['nama_barang'] . '</td>
<td>' . $row['nama_jenis'] . '</td>
<td class="text-center">' . $row['jumlah'] . '</td>
<td class="text-right">' . number_format($row['harga'], 0, ',', '.') . '</td>
<td class="text-right">' . number_format($total, 0, ',', '.') . '</td>
</tr>';
}
echo '<tr>
<td colspan="7" class="text-right"><strong>Grand Total</strong></td>
<td class="text-right"><strong>' . number_format($grand_total, 0, ',', '.') . '</strong></td>
</tr>';
} else {
echo '<tr><td colspan="8" class="text-center">Tidak ada data laporan</td></tr>';
}
echo '</tbody>
</table>
</body>
</html>';
// Close database connection
$conn->close();
?>

4
views/barang/tes.php Normal file
View File

@ -0,0 +1,4 @@
<?php
require_once('topsis_restok.php');
tampilkanSaranRestok();
?>

View File

@ -0,0 +1,174 @@
<?php
function restockWithTOPSIS($conn, $criteria_weights = null) {
// Bobot default (sesuai manual)
if ($criteria_weights === null) {
$criteria_weights = [
'stock_level' => 0.4,
'price_value' => 0.3,
'turnover_rate' => 0.3,
];
}
// Ambil data produk dan penjualan 30 hari terakhir sesuai struktur DB
$query = "
SELECT
bk.id_barangK AS id_barang,
bk.nama_barang,
bk.stok,
bk.harga,
COALESCE(SUM(dt.jumlah), 0) AS total_penjualan
FROM barang_kasir bk
LEFT JOIN detail_transaksi dt ON bk.id_barangK = dt.id_barangK
LEFT JOIN transaksi t ON dt.id_transaksi = t.id_transaksi
AND t.tanggal BETWEEN DATE_SUB(NOW(), INTERVAL 30 DAY) AND NOW()
GROUP BY bk.id_barangK, bk.nama_barang, bk.stok, bk.harga
";
$result = mysqli_query($conn, $query);
if (!$result) {
return ["error" => "Query failed: " . mysqli_error($conn)];
}
$products = [];
while ($row = mysqli_fetch_assoc($result)) {
// Pastikan harga sudah dalam format numeric (decimal)
$harga = (float)$row['harga'];
// Hitung turnover rate (penjualan/stok), hindari pembagian 0
$turnover_rate = ($row['stok'] > 0) ? ($row['total_penjualan'] / $row['stok']) : 0;
$products[] = [
'id_barang' => $row['id_barang'],
'nama_barang' => $row['nama_barang'],
'stok' => (int)$row['stok'],
'harga' => $harga,
'turnover_rate' => $turnover_rate,
];
}
if (empty($products)) {
return ["message" => "No products found"];
}
// Buat decision matrix dengan reciprocal stok (1/stok)
$decision_matrix = [];
foreach ($products as $product) {
$reciprocal_stock = ($product['stok'] > 0) ? (1 / $product['stok']) : 0;
$decision_matrix[] = [
'id_barang' => $product['id_barang'],
'nama_barang' => $product['nama_barang'],
'stock_level' => $reciprocal_stock,
'price_value' => $product['harga'],
'turnover_rate' => $product['turnover_rate'],
];
}
// Normalisasi
$normalized_matrix = normalizeMatrix($decision_matrix);
// Terapkan bobot
$weighted_matrix = applyWeights($normalized_matrix, $criteria_weights);
// Tentukan solusi ideal positif dan negatif
$ideal_solution = [];
$negative_ideal_solution = [];
foreach (['stock_level', 'price_value', 'turnover_rate'] as $criteria) {
$values = array_column($weighted_matrix, $criteria);
$ideal_solution[$criteria] = max($values);
$negative_ideal_solution[$criteria] = min($values);
}
// Hitung jarak ke solusi ideal positif dan negatif
$separation_ideal = [];
$separation_negative = [];
foreach ($weighted_matrix as $key => $product) {
$separation_ideal[$key] = sqrt(
pow($product['stock_level'] - $ideal_solution['stock_level'], 2) +
pow($product['price_value'] - $ideal_solution['price_value'], 2) +
pow($product['turnover_rate'] - $ideal_solution['turnover_rate'], 2)
);
$separation_negative[$key] = sqrt(
pow($product['stock_level'] - $negative_ideal_solution['stock_level'], 2) +
pow($product['price_value'] - $negative_ideal_solution['price_value'], 2) +
pow($product['turnover_rate'] - $negative_ideal_solution['turnover_rate'], 2)
);
}
// Hitung skor TOPSIS (relative closeness)
$relative_closeness = [];
foreach ($weighted_matrix as $key => $product) {
$denominator = $separation_negative[$key] + $separation_ideal[$key];
$relative_closeness[$key] = ($denominator > 0) ? ($separation_negative[$key] / $denominator) : 0;
}
// Siapkan hasil
$topsis_scores = [];
foreach ($relative_closeness as $key => $score) {
$topsis_scores[] = [
'id_barang' => $decision_matrix[$key]['id_barang'],
'nama_barang' => $decision_matrix[$key]['nama_barang'],
'stok' => $products[$key]['stok'],
'harga' => $products[$key]['harga'],
'topsis_score' => round($score, 4),
'rank' => 0,
];
}
// Urutkan berdasarkan skor TOPSIS descending
usort($topsis_scores, function($a, $b) {
return $b['topsis_score'] <=> $a['topsis_score'];
});
// Beri peringkat
foreach ($topsis_scores as $key => $product) {
$topsis_scores[$key]['rank'] = $key + 1;
}
return $topsis_scores;
}
function normalizeMatrix($matrix) {
$normalized = [];
$sum_squares = [
'stock_level' => 0,
'price_value' => 0,
'turnover_rate' => 0,
];
foreach ($matrix as $product) {
$sum_squares['stock_level'] += pow($product['stock_level'], 2);
$sum_squares['price_value'] += pow($product['price_value'], 2);
$sum_squares['turnover_rate'] += pow($product['turnover_rate'], 2);
}
foreach ($sum_squares as $key => $value) {
$sum_squares[$key] = sqrt($value);
}
foreach ($matrix as $key => $product) {
$normalized[$key] = [
'id_barang' => $product['id_barang'],
'nama_barang' => $product['nama_barang'],
'stock_level' => ($sum_squares['stock_level'] > 0) ? ($product['stock_level'] / $sum_squares['stock_level']) : 0,
'price_value' => ($sum_squares['price_value'] > 0) ? ($product['price_value'] / $sum_squares['price_value']) : 0,
'turnover_rate' => ($sum_squares['turnover_rate'] > 0) ? ($product['turnover_rate'] / $sum_squares['turnover_rate']) : 0,
];
}
return $normalized;
}
function applyWeights($matrix, $weights) {
$weighted = [];
foreach ($matrix as $key => $product) {
$weighted[$key] = [
'id_barang' => $product['id_barang'],
'nama_barang' => $product['nama_barang'],
'stock_level' => $product['stock_level'] * $weights['stock_level'],
'price_value' => $product['price_value'] * $weights['price_value'],
'turnover_rate' => $product['turnover_rate'] * $weights['turnover_rate'],
];
}
return $weighted;
}
?>

View File

@ -0,0 +1,141 @@
<?php
// File: topsis_implementation.php
require_once 'topsis_functions.php';
function processRestock($conn, $productId, $amount, $note = '') {
$productId = (int)$productId;
$amount = (int)$amount;
if ($productId <= 0 || $amount <= 0) {
return [
'success' => false,
'message' => 'Invalid product ID or amount'
];
}
// Ambil stok sekarang dari barang_kasir
$query = "SELECT stok FROM barang_kasir WHERE id_barangK = ?";
$stmt = mysqli_prepare($conn, $query);
mysqli_stmt_bind_param($stmt, "i", $productId);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
if (!$row = mysqli_fetch_assoc($result)) {
return [
'success' => false,
'message' => 'Product not found'
];
}
$currentStock = (int)$row['stok'];
$newStock = $currentStock + $amount;
// Update stok barang_kasir
$updateQuery = "UPDATE barang_kasir SET stok = ? WHERE id_barangK = ?";
$updateStmt = mysqli_prepare($conn, $updateQuery);
mysqli_stmt_bind_param($updateStmt, "ii", $newStock, $productId);
$success = mysqli_stmt_execute($updateStmt);
if (!$success) {
return [
'success' => false,
'message' => 'Failed to update stock: ' . mysqli_error($conn)
];
}
// Log aktivitas restok
$logQuery = "INSERT INTO log_restok (id_barang, jumlah, tanggal, catatan)
VALUES (?, ?, NOW(), ?)";
$logStmt = mysqli_prepare($conn, $logQuery);
mysqli_stmt_bind_param($logStmt, "iis", $productId, $amount, $note);
mysqli_stmt_execute($logStmt);
return [
'success' => true,
'message' => 'Stock updated successfully',
'new_stock' => $newStock
];
}
// Handle AJAX request
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$host = "localhost";
$username = "root"; // sesuaikan username DB Anda
$password = ""; // sesuaikan password DB Anda
$database = "ayula_store";
$conn = mysqli_connect($host, $username, $password, $database);
if (!$conn) {
echo json_encode([
'success' => false,
'message' => 'Database connection failed: ' . mysqli_connect_error()
]);
exit;
}
$action = $_POST['action'];
switch ($action) {
case 'restock':
if (isset($_POST['productId']) && isset($_POST['amount'])) {
$result = processRestock(
$conn,
$_POST['productId'],
$_POST['amount'],
$_POST['note'] ?? ''
);
echo json_encode($result);
} else {
echo json_encode([
'success' => false,
'message' => 'Missing required parameters'
]);
}
break;
default:
echo json_encode([
'success' => false,
'message' => 'Unknown action'
]);
}
mysqli_close($conn);
exit;
}
function createRestockLogTable($conn) {
$query = "CREATE TABLE IF NOT EXISTS log_restok (
id INT(11) PRIMARY KEY AUTO_INCREMENT,
id_barang INT(11) NOT NULL,
jumlah INT(10) NOT NULL,
tanggal DATETIME NOT NULL,
catatan TEXT,
FOREIGN KEY (id_barang) REFERENCES barang_kasir(id_barangK)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
return mysqli_query($conn, $query);
}
function exportToCSV($data, $filename = 'topsis_results.csv') {
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="' . $filename . '"');
$output = fopen('php://output', 'w');
fputcsv($output, ['Rank', 'ID Barang', 'Nama Barang', 'Stok', 'Harga', 'TOPSIS Score']);
foreach ($data as $row) {
fputcsv($output, [
$row['rank'],
$row['id_barang'],
$row['nama_barang'],
$row['stok'],
$row['harga'],
$row['topsis_score']
]);
}
fclose($output);
exit;
}
?>

View File

@ -0,0 +1,627 @@
<?php
// File: topsis_restock_view.php
// Include the TOPSIS function
require_once 'topsis_functions.php';
// Database connection
$host = "localhost";
$username = "root";
$password = "";
$database = "ayula_store";
$conn = mysqli_connect($host, $username, $password, $database);
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
// Default criteria weights
$default_weights = [
'stock_level' => 0.4, // Lower stock is more urgent (40% importance)
'price_value' => 0.3, // Higher price might indicate higher priority (30% importance)
'turnover_rate' => 0.3 // Higher turnover rate means faster selling (30% importance)
];
// Check if form was submitted with custom weights
$weights = $default_weights;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_weights'])) {
$weights = [
'stock_level' => floatval($_POST['weight_stock']),
'price_value' => floatval($_POST['weight_price']),
'turnover_rate' => floatval($_POST['weight_turnover'])
];
// Normalize weights to ensure they sum to 1
$total = array_sum($weights);
if ($total > 0) {
foreach ($weights as $key => $value) {
$weights[$key] = $value / $total;
}
} else {
$weights = $default_weights;
}
}
// Get restocking priorities using TOPSIS
$restock_priorities = restockWithTOPSIS($conn, $weights);
// Check if any error occurred
$error_message = "";
if (isset($restock_priorities['error'])) {
$error_message = $restock_priorities['error'];
$restock_priorities = [];
}
// Calculate total products and those needing restocking (stock < 10)
$total_products = count($restock_priorities);
$low_stock_count = 0;
foreach ($restock_priorities as $product) {
if ($product['stok'] < 10) {
$low_stock_count++;
}
}
// Get current date and time
$current_date = date('Y-m-d H:i:s');
// Page title
$page_title = "Analisis Restok dengan Metode TOPSIS";
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0" />
<meta name="description" content="POS - Bootstrap Admin Template" />
<meta
name="keywords"
content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects" />
<meta name="author" content="Dreamguys - Bootstrap Admin Template" />
<meta name="robots" content="noindex, nofollow" />
<title>Dreams Pos admin template</title>
<link
rel="shortcut icon"
type="image/x-icon"
href="/ayula-store/bootstrap/assets/img/favicon.jpg" />
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/animate.css" />
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/select2/css/select2.min.css" />
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/dataTables.bootstrap4.min.css" />
<link
rel="stylesheet"
href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css" />
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/all.min.css" />
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/style.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
<style>
.priority-high {
background-color: #ffdddd !important;
}
.priority-medium {
background-color: #ffffdd !important;
}
.priority-low {
background-color: #ddffdd !important;
}
.weights-form {
background-color: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.dashboard-card {
transition: transform 0.3s;
}
.dashboard-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
#weightChart {
max-width: 300px;
margin: 0 auto;
}
.restock-btn {
background-color: #007bff; /* Blue background */
color: white; /* White text */
border: none; /* Remove the border */
padding: 8px 16px; /* Adjust padding */
font-size: 14px; /* Adjust font size */
border-radius: 5px; /* Rounded corners */
transition: background-color 0.3s ease; /* Smooth hover effect */
}
.restock-btn:hover {
background-color: #0056b3; /* Darker blue on hover */
}
/* Table Header Styling */
.table th {
color: white !important; /* Ensures the text color in the header is white */
background-color: #343a40 !important; /* Dark background for the header */
}
/* Table Cells Styling */
.table td {
color: #000000 !important; /* Text color set to black for the table cells */
}
/* Row Priority Class - High Priority (Red Background) */
.priority-high td {
background-color: #ffdddd !important; /* Red background for high priority */
color: black !important; /* Black text color */
}
/* Row Priority Class - Medium Priority (Yellow Background) */
.priority-medium td {
background-color: #ffffdd !important; /* Yellow background for medium priority */
color: black !important; /* Black text color */
}
/* Row Priority Class - Low Priority (Green Background) */
.priority-low td {
background-color: #ddffdd !important; /* Green background for low priority */
color: black !important; /* Black text color */
}
/* Optional: Styling the action buttons */
.table td button {
color: white; /* Make button text white */
background-color: #007bff; /* Blue background for buttons */
border: none;
padding: 5px 10px;
border-radius: 5px;
}
.table td button:hover {
background-color: #0056b3; /* Darker blue on hover */
}
</style>
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"></div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($displayName); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
aria-expanded="false"><i class="fa fa-ellipsis-v"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/reporttt/report.php"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Barang</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php" >Daftar Barang</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Barang</a></li>
</ul>
</li>
<li class="active">
<a href="/ayula-store/views/barang/topsis_restock_view.php"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
Analisa Barang</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<h1 class="mb-4 text-center"><?php echo $page_title; ?></h1>
<div class="row mb-4">
<div class="col-md-4">
<div class="card text-white bg-primary dashboard-card h-100">
<div class="card-body text-center">
<h5 class="card-title">Total Produk</h5>
<h2><?php echo $total_products; ?></h2>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-white bg-danger dashboard-card h-100">
<div class="card-body text-center">
<h5 class="card-title">Produk Stok Rendah</h5>
<h2><?php echo $low_stock_count; ?></h2>
<small>Stok < 10</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card text-white bg-info dashboard-card h-100">
<div class="card-body text-center">
<h5 class="card-title">Tanggal Analisis</h5>
<p><?php echo $current_date; ?></p>
</div>
</div>
</div>
</div>
<div class="row mb-4">
<div class="col-md-8">
<div class="weights-form">
<h4>Kustomisasi Bobot Kriteria</h4>
<form method="POST" action="" class="row g-3">
<div class="col-md-4">
<label for="weight_stock" class="form-label">Bobot Stok:</label>
<input type="number" class="form-control" id="weight_stock" name="weight_stock"
min="0" max="10" step="0.1" value="<?php echo $weights['stock_level'] * 10; ?>">
<small class="text-muted">Nilai lebih tinggi = stok rendah lebih penting</small>
</div>
<div class="col-md-4">
<label for="weight_price" class="form-label">Bobot Harga:</label>
<input type="number" class="form-control" id="weight_price" name="weight_price"
min="0" max="10" step="0.1" value="<?php echo $weights['price_value'] * 10; ?>">
<small class="text-muted">Nilai lebih tinggi = harga tinggi lebih penting</small>
</div>
<div class="col-md-4">
<label for="weight_turnover" class="form-label">Bobot Perputaran:</label>
<input type="number" class="form-control" id="weight_turnover" name="weight_turnover"
min="0" max="10" step="0.1" value="<?php echo $weights['turnover_rate'] * 10; ?>">
<small class="text-muted">Nilai lebih tinggi = penjualan cepat lebih penting</small>
</div>
<div class="col-12 mt-3">
<button type="submit" name="submit_weights" class="btn btn-primary">Terapkan Bobot</button>
<button type="button" class="btn btn-secondary" onclick="resetWeights()">Reset ke Default</button>
</div>
</form>
</div>
</div>
<div class="col-md-4">
<div class="card h-100">
<div class="card-header">
Distribusi Bobot
</div>
<div class="card-body">
<canvas id="weightChart"></canvas>
</div>
</div>
</div>
</div>
<?php if ($error_message): ?>
<div class="alert alert-danger" role="alert">
<?php echo $error_message; ?>
</div>
<?php else: ?>
<div class="card mb-4">
<div class="card-header">
<h4>Hasil Analisis TOPSIS</h4>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover table-striped">
<thead class="table-dark">
<tr>
<th>Prioritas</th>
<th>ID Barang</th>
<th>Nama Barang</th>
<th>Stok Saat Ini</th>
<th>Harga</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
<?php foreach ($restock_priorities as $product):
// Determine priority class based on TOPSIS score
$priority_class = '';
if ($product['topsis_score'] > 0.7) {
$priority_class = 'priority-high';
} elseif ($product['topsis_score'] > 0.4) {
$priority_class = 'priority-medium';
} else {
$priority_class = 'priority-low';
}
?>
<tr class="<?php echo $priority_class; ?>">
<td><?php echo $product['rank']; ?></td>
<td><?php echo $product['id_barang']; ?></td>
<td><?php echo $product['nama_barang']; ?></td>
<td><?php echo $product['stok']; ?></td>
<td>Rp <?php echo number_format($product['harga'], 0, ',', '.'); ?></td>
<td>
<button class=" restock-btn" onclick="openRestockModal(<?php echo $product['id_barang']; ?>, '<?php echo $product['nama_barang']; ?>')">
Restok
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Export Options -->
<?php endif; ?>
</div>
<div class="modal fade" id="restockModal" tabindex="-1" aria-labelledby="restockModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="restockModalLabel">Restok Produk</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="restockForm">
<input type="hidden" id="productId" name="productId">
<div class="mb-3">
<label for="productName" class="form-label">Nama Produk</label>
<input type="text" class="form-control" id="productName" readonly>
</div>
<div class="mb-3">
<label for="restockAmount" class="form-label">Jumlah Restok</label>
<input type="number" class="form-control" id="restockAmount" name="restockAmount" min="1" required>
</div>
<div class="mb-3">
<label for="supplierNote" class="form-label">Catatan</label>
<textarea class="form-control" id="supplierNote" name="supplierNote" rows="3"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="button" class="btn btn-primary" onclick="submitRestock()">Proses Restok</button>
</div>
</div>
</div>
</div>
<!-- JavaScript -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/feather.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/sweetalert/sweetalert2.all.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/sweetalert/sweetalerts.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/script.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// Initialize modal
const restockModal = new bootstrap.Modal(document.getElementById('restockModal'));
// Open restock modal
function openRestockModal(productId, productName) {
document.getElementById('productId').value = productId;
document.getElementById('productName').value = productName;
document.getElementById('restockAmount').value = '10'; // Default value
document.getElementById('supplierNote').value = '';
restockModal.show();
}
// Submit restock form
function submitRestock() {
const productId = document.getElementById('productId').value;
const amount = document.getElementById('restockAmount').value;
const note = document.getElementById('supplierNote').value;
// Here you would typically send an AJAX request to update the database
alert(`Produk ID: ${productId} akan direstok dengan jumlah: ${amount}`);
// In a real implementation, you would use fetch or XMLHttpRequest to send to server
// Example:
/*
fetch('process_restock.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `productId=${productId}&amount=${amount}&note=${encodeURIComponent(note)}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Restok berhasil!');
window.location.reload();
} else {
alert('Error: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Terjadi kesalahan saat memproses restok.');
});
*/
restockModal.hide();
}
// Reset weights to default
function resetWeights() {
document.getElementById('weight_stock').value = '4';
document.getElementById('weight_price').value = '3';
document.getElementById('weight_turnover').value = '3';
}
// Export table to CSV/PDF
function exportTable(format) {
alert(`Mengekspor tabel ke format ${format.toUpperCase()}`);
// Implement actual export functionality here
}
// Print table
function printTable() {
window.print();
}
// Initialize weight chart
const ctx = document.getElementById('weightChart').getContext('2d');
const weightChart = new Chart(ctx, {
type: 'pie',
data: {
labels: ['Stok', 'Harga', 'Perputaran'],
datasets: [{
data: [
<?php echo $weights['stock_level']; ?>,
<?php echo $weights['price_value']; ?>,
<?php echo $weights['turnover_rate']; ?>
],
backgroundColor: [
'rgba(255, 99, 132, 0.7)',
'rgba(54, 162, 235, 0.7)',
'rgba(255, 206, 86, 0.7)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom',
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.formattedValue || '';
return `${label}: ${value}`;
}
}
}
}
}
});
function submitRestock() {
// Get the values from the modal form
var productName = document.getElementById('productName').value;
var restockAmount = document.getElementById('restockAmount').value;
var supplierNote = document.getElementById('supplierNote').value;
// Validate form fields before proceeding
if (!productName || !restockAmount || !supplierNote) {
alert('Please fill out all fields.');
return;
}
// Prepare the message to be sent via WhatsApp
var message = "Restok Produk:\n";
message += "Nama Produk: " + productName + "\n";
message += "Jumlah Restok: " + restockAmount + "\n";
message += "Catatan : " + supplierNote;
// Encode the message for URL (using encodeURIComponent for proper encoding)
var encodedMessage = encodeURIComponent(message);
// Define the phone number for WhatsApp (replace with your desired phone number)
var phoneNumber = "+6287857242169"; // Replace with the recipient's phone number (without + sign)
// WhatsApp API URL
var whatsappURL = "https://wa.me/" + phoneNumber + "?text=" + encodedMessage;
// Open WhatsApp with the message
window.open(whatsappURL, "_blank");
}
</script>
</body>
</html>

334
views/barang/transfer.php Normal file
View File

@ -0,0 +1,334 @@
<?php
// Updated transfer.php file that handles both single and bulk transfers
// Save this in the same directory as your productlist.php
// Set headers for JSON response
header('Content-Type: application/json');
// Create log file
function log_debug($message) {
file_put_contents('transfer_debug.log', date('[Y-m-d H:i:s] ') . $message . "\n", FILE_APPEND);
}
log_debug('Script started');
log_debug('POST data: ' . json_encode($_POST));
// Database connection
$servername = "localhost";
$username = "root";
$password = "";
$database = "ayula_store";
// Connect to database
$conn = new mysqli($servername, $username, $password, $database);
if ($conn->connect_error) {
log_debug("Connection failed: " . $conn->connect_error);
echo json_encode([
'success' => false,
'message' => 'Koneksi database gagal: ' . $conn->connect_error
]);
exit;
}
log_debug("Database connected successfully");
// Check if we're handling a single product or bulk transfer
$is_bulk = isset($_POST['product_ids']) && is_array($_POST['product_ids']);
if ($is_bulk) {
// Bulk transfer
log_debug("Processing bulk transfer");
$product_ids = $_POST['product_ids'];
$quantities = $_POST['quantities'];
// Validate input
if (empty($product_ids) || empty($quantities) || count($product_ids) != count($quantities)) {
log_debug("Invalid bulk data");
echo json_encode([
'success' => false,
'message' => 'Data tidak valid untuk transfer massal.'
]);
exit;
}
// Begin transaction
$conn->begin_transaction();
$success_count = 0;
$error_messages = [];
for ($i = 0; $i < count($product_ids); $i++) {
$id_barang = $product_ids[$i];
$quantity = intval($quantities[$i]);
log_debug("Processing item $i: ID=$id_barang, Quantity=$quantity");
// Skip invalid items
if (empty($id_barang) || $quantity <= 0) {
$error_messages[] = "Data tidak valid untuk produk #$i";
continue;
}
// Get product details
$stmt = $conn->prepare("SELECT * FROM barang WHERE id_barang = ?");
$stmt->bind_param("s", $id_barang);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 0) {
$error_messages[] = "Produk dengan ID $id_barang tidak ditemukan";
$stmt->close();
continue;
}
$product = $result->fetch_assoc();
// Check stock
if ($quantity > $product['stok']) {
$error_messages[] = "Stok tidak mencukupi untuk produk {$product['nama_barang']}";
$stmt->close();
continue;
}
// Update stock in barang
$new_stock = $product['stok'] - $quantity;
$update_stmt = $conn->prepare("UPDATE barang SET stok = ? WHERE id_barang = ?");
$update_stmt->bind_param("is", $new_stock, $id_barang);
if (!$update_stmt->execute()) {
$error_messages[] = "Gagal memperbarui stok untuk produk {$product['nama_barang']}";
$stmt->close();
$update_stmt->close();
continue;
}
$update_stmt->close();
// Check if exists in barang_kasir
$check_stmt = $conn->prepare("SELECT * FROM barang_kasir WHERE kode_barang = ?");
$check_stmt->bind_param("s", $product['kode_barang']);
$check_stmt->execute();
$check_result = $check_stmt->get_result();
$timestamp = date('Y-m-d H:i:s');
if ($check_result->num_rows > 0) {
// Update existing record
$kasir_product = $check_result->fetch_assoc();
$new_kasir_stock = $kasir_product['stok'] + $quantity;
$kasir_update_sql = "UPDATE barang_kasir SET stok = ?, update_at = ? WHERE id_barangK = ?";
$kasir_update_stmt = $conn->prepare($kasir_update_sql);
$kasir_update_stmt->bind_param("isi", $new_kasir_stock, $timestamp, $kasir_product['id_barangK']);
if (!$kasir_update_stmt->execute()) {
$error_messages[] = "Gagal memperbarui stok di kasir untuk produk {$product['nama_barang']}";
$stmt->close();
$check_stmt->close();
$kasir_update_stmt->close();
continue;
}
$kasir_update_stmt->close();
} else {
// Insert new record
// Using direct query to avoid parameter binding issues
$kode_barang = $conn->real_escape_string($product['kode_barang']);
$nama_barang = $conn->real_escape_string($product['nama_barang']);
$id_jenis = (int)$product['id_jenis'];
$harga = $conn->real_escape_string($product['harga']);
$stok = (int)$quantity;
$gambar = $conn->real_escape_string($product['image'] ?? '');
$timestamp_esc = $conn->real_escape_string($timestamp);
$insert_query = "INSERT INTO barang_kasir (kode_barang, nama_barang, id_jenis, harga, stok, gambar, created_at, update_at)
VALUES ('$kode_barang', '$nama_barang', $id_jenis, '$harga', $stok, '$gambar', '$timestamp_esc', '$timestamp_esc')";
if (!$conn->query($insert_query)) {
$error_messages[] = "Gagal menambahkan produk {$product['nama_barang']} ke kasir";
$stmt->close();
$check_stmt->close();
continue;
}
}
$check_stmt->close();
$stmt->close();
$success_count++;
}
if ($success_count == 0) {
// If no products were successfully processed
$conn->rollback();
log_debug("Bulk transfer failed: " . implode(', ', $error_messages));
echo json_encode([
'success' => false,
'message' => 'Tidak ada produk yang berhasil dipindahkan: ' . implode(', ', $error_messages)
]);
} else {
// Commit if at least one product was processed
$conn->commit();
if (count($error_messages) > 0) {
log_debug("Partial bulk transfer success: $success_count items, with errors: " . implode(', ', $error_messages));
echo json_encode([
'success' => true,
'message' => "Berhasil memindahkan $success_count produk ke kasir. Beberapa produk gagal: " . implode(', ', $error_messages)
]);
} else {
log_debug("Complete bulk transfer success: $success_count items");
echo json_encode([
'success' => true,
'message' => "Berhasil memindahkan $success_count produk ke kasir."
]);
}
}
} else {
// Single product transfer
if (!isset($_POST['id_barang']) || !isset($_POST['quantity'])) {
log_debug("Missing required parameters for single transfer");
echo json_encode([
'success' => false,
'message' => 'Parameter tidak lengkap'
]);
exit;
}
$id_barang = $_POST['id_barang'];
$quantity = (int)$_POST['quantity'];
log_debug("Processing single transfer for product ID: $id_barang, quantity: $quantity");
// Validate data
if (empty($id_barang) || $quantity <= 0) {
log_debug("Invalid parameters");
echo json_encode([
'success' => false,
'message' => 'Parameter tidak valid'
]);
exit;
}
// Get product information
$stmt = $conn->prepare("SELECT * FROM barang WHERE id_barang = ?");
$stmt->bind_param("s", $id_barang);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 0) {
log_debug("Product not found");
echo json_encode([
'success' => false,
'message' => 'Barang tidak ditemukan'
]);
$stmt->close();
exit;
}
$product = $result->fetch_assoc();
log_debug("Product found: " . json_encode($product));
// Check if enough stock
if ($quantity > $product['stok']) {
log_debug("Insufficient stock");
echo json_encode([
'success' => false,
'message' => 'Stok tidak mencukupi'
]);
$stmt->close();
exit;
}
// Begin transaction
$conn->begin_transaction();
try {
// 1. Update stock in barang
$new_stock = $product['stok'] - $quantity;
$update_stmt = $conn->prepare("UPDATE barang SET stok = ? WHERE id_barang = ?");
$update_stmt->bind_param("is", $new_stock, $id_barang);
if (!$update_stmt->execute()) {
throw new Exception("Failed to update product stock: " . $update_stmt->error);
}
log_debug("Updated barang stock to: $new_stock");
$update_stmt->close();
// 2. Check if product exists in barang_kasir
$check_stmt = $conn->prepare("SELECT * FROM barang_kasir WHERE kode_barang = ?");
$check_stmt->bind_param("s", $product['kode_barang']);
$check_stmt->execute();
$check_result = $check_stmt->get_result();
$timestamp = date('Y-m-d H:i:s');
if ($check_result->num_rows > 0) {
// 3a. Update existing product
$kasir_product = $check_result->fetch_assoc();
$new_kasir_stock = $kasir_product['stok'] + $quantity;
log_debug("Product exists in cashier, updating stock to: $new_kasir_stock");
$kasir_update_stmt = $conn->prepare("UPDATE barang_kasir SET stok = ?, update_at = ? WHERE id_barangK = ?");
$kasir_update_stmt->bind_param("isi", $new_kasir_stock, $timestamp, $kasir_product['id_barangK']);
if (!$kasir_update_stmt->execute()) {
throw new Exception("Failed to update cashier stock: " . $kasir_update_stmt->error);
}
$kasir_update_stmt->close();
} else {
// 3b. Insert new product with direct query
log_debug("Product doesn't exist in cashier, inserting new record");
// Set default empty string for image
$kode_barang = $conn->real_escape_string($product['kode_barang']);
$nama_barang = $conn->real_escape_string($product['nama_barang']);
$id_jenis = (int)$product['id_jenis'];
$harga = $conn->real_escape_string($product['harga']);
$stok = (int)$quantity;
$gambar = $conn->real_escape_string($product['image'] ?? '');
$timestamp_esc = $conn->real_escape_string($timestamp);
$insert_query = "INSERT INTO barang_kasir (kode_barang, nama_barang, id_jenis, harga, stok, gambar, created_at, update_at)
VALUES ('$kode_barang', '$nama_barang', $id_jenis, '$harga', $stok, '$gambar', '$timestamp_esc', '$timestamp_esc')";
log_debug("Insert query: $insert_query");
if (!$conn->query($insert_query)) {
throw new Exception("Failed to insert product into cashier: " . $conn->error);
}
}
$check_stmt->close();
// Commit transaction
$conn->commit();
log_debug("Transfer completed successfully");
echo json_encode([
'success' => true,
'message' => 'Berhasil memindahkan ' . $quantity . ' item ke kasir'
]);
} catch (Exception $e) {
// Rollback on error
$conn->rollback();
log_debug("Error occurred: " . $e->getMessage());
echo json_encode([
'success' => false,
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
]);
}
$stmt->close();
}
$conn->close();
log_debug('Script completed');
?>

View File

@ -0,0 +1,52 @@
[2025-06-01 09:32:55] Script started
[2025-06-01 09:32:55] POST data: {"id_barang":"120","quantity":"1"}
[2025-06-01 09:32:55] Database connected successfully
[2025-06-01 09:32:55] Processing single transfer for product ID: 120, quantity: 1
[2025-06-01 09:32:55] Product found: {"id_barang":120,"kode_barang":"BRG039","nama_barang":"Produk A","stok":5,"harga":"150000","id_jenis":8,"image":"kios-Halaman-2.drawio (2).png"}
[2025-06-01 09:32:55] Updated barang stock to: 4
[2025-06-01 09:32:55] Product exists in cashier, updating stock to: 6
[2025-06-01 09:32:55] Transfer completed successfully
[2025-06-01 09:32:55] Script completed
[2025-06-01 09:33:23] Script started
[2025-06-01 09:33:23] POST data: {"product_ids":["120","121"],"quantities":["1","1"]}
[2025-06-01 09:33:23] Database connected successfully
[2025-06-01 09:33:23] Processing bulk transfer
[2025-06-01 09:33:23] Processing item 0: ID=120, Quantity=1
[2025-06-01 09:33:23] Processing item 1: ID=121, Quantity=1
[2025-06-01 09:33:23] Complete bulk transfer success: 2 items
[2025-06-01 09:33:23] Script completed
[2025-06-01 09:33:33] Script started
[2025-06-01 09:33:33] POST data: {"id_barang":"122","quantity":"1"}
[2025-06-01 09:33:33] Database connected successfully
[2025-06-01 09:33:33] Processing single transfer for product ID: 122, quantity: 1
[2025-06-01 09:33:33] Product found: {"id_barang":122,"kode_barang":"BRG041","nama_barang":"Produk B","stok":12,"harga":"75000","id_jenis":1,"image":"kios-Halaman-3.drawio (2).png"}
[2025-06-01 09:33:33] Updated barang stock to: 11
[2025-06-01 09:33:33] Product exists in cashier, updating stock to: 13
[2025-06-01 09:33:33] Transfer completed successfully
[2025-06-01 09:33:33] Script completed
[2025-06-01 21:07:57] Script started
[2025-06-01 21:07:57] POST data: {"id_barang":"120","quantity":"1"}
[2025-06-01 21:07:57] Database connected successfully
[2025-06-01 21:07:57] Processing single transfer for product ID: 120, quantity: 1
[2025-06-01 21:07:57] Product found: {"id_barang":120,"kode_barang":"BRG039","nama_barang":"Produk A","stok":3,"harga":"150000","id_jenis":8,"image":"product_1748803380.png"}
[2025-06-01 21:07:57] Updated barang stock to: 2
[2025-06-01 21:07:57] Product exists in cashier, updating stock to: 8
[2025-06-01 21:07:57] Transfer completed successfully
[2025-06-01 21:07:57] Script completed
[2025-06-01 21:08:07] Script started
[2025-06-01 21:08:07] POST data: {"product_ids":["122","124"],"quantities":["1","1"]}
[2025-06-01 21:08:07] Database connected successfully
[2025-06-01 21:08:07] Processing bulk transfer
[2025-06-01 21:08:07] Processing item 0: ID=122, Quantity=1
[2025-06-01 21:08:07] Processing item 1: ID=124, Quantity=1
[2025-06-01 21:08:07] Complete bulk transfer success: 2 items
[2025-06-01 21:08:07] Script completed
[2025-06-03 03:21:32] Script started
[2025-06-03 03:21:32] POST data: {"id_barang":"122","quantity":"1"}
[2025-06-03 03:21:32] Database connected successfully
[2025-06-03 03:21:32] Processing single transfer for product ID: 122, quantity: 1
[2025-06-03 03:21:32] Product found: {"id_barang":122,"kode_barang":"BRG041","nama_barang":"Produk B","stok":10,"harga":"75000","id_jenis":1,"image":"kios-Halaman-3.drawio (2).png"}
[2025-06-03 03:21:32] Updated barang stock to: 9
[2025-06-03 03:21:32] Product exists in cashier, updating stock to: 15
[2025-06-03 03:21:32] Transfer completed successfully
[2025-06-03 03:21:32] Script completed

View File

@ -0,0 +1,216 @@
<?php
// transfer_handler.php
// File to handle individual and bulk product transfers to cashier
// Koneksi ke database
$servername = "localhost";
$username = "root"; // Sesuaikan dengan username database kamu
$password = ""; // Sesuaikan dengan password database kamu
$database = "ayula_store"; // Sesuaikan dengan nama database kamu
$conn = new mysqli($servername, $username, $password, $database);
// Periksa koneksi
if ($conn->connect_error) {
die(json_encode([
'success' => false,
'message' => "Koneksi gagal: " . $conn->connect_error
]));
}
// Fungsi untuk memvalidasi data yang diterima
function validateData($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
// Handle single product transfer
if (isset($_POST['action']) && $_POST['action'] == 'transfer_product') {
// Ambil data dari POST
$id_barang = validateData($_POST['id_barang']);
$quantity = intval($_POST['quantity']);
// Validasi input
if (empty($id_barang) || $quantity <= 0) {
echo json_encode([
'success' => false,
'message' => 'Data tidak valid.'
]);
exit;
}
// Check if stock is sufficient
$sql = "SELECT stok FROM barang WHERE id_barang = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id_barang);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 0) {
echo json_encode([
'success' => false,
'message' => 'Barang tidak ditemukan.'
]);
$stmt->close();
exit;
}
$row = $result->fetch_assoc();
$current_stock = $row['stok'];
if ($quantity > $current_stock) {
echo json_encode([
'success' => false,
'message' => 'Jumlah melebihi stok tersedia.'
]);
$stmt->close();
exit;
}
// Begin transaction
$conn->begin_transaction();
try {
// Update stock in barang table
$new_stock = $current_stock - $quantity;
$update_sql = "UPDATE barang SET stok = ? WHERE id_barang = ?";
$update_stmt = $conn->prepare($update_sql);
$update_stmt->bind_param("is", $new_stock, $id_barang);
$update_stmt->execute();
// TODO: Insert data to cashier table or perform other actions as needed
// This will depend on your specific requirements for transferring to cashier
// Commit the transaction
$conn->commit();
echo json_encode([
'success' => true,
'message' => 'Berhasil memindahkan ' . $quantity . ' item ke kasir.'
]);
} catch (Exception $e) {
// Rollback transaction on error
$conn->rollback();
echo json_encode([
'success' => false,
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
]);
}
$stmt->close();
if (isset($update_stmt)) {
$update_stmt->close();
}
}
// Handle bulk product transfers
if (isset($_POST['action']) && $_POST['action'] == 'bulk_transfer_products') {
// Get product IDs and quantities
$product_ids = isset($_POST['product_ids']) ? $_POST['product_ids'] : [];
$quantities = isset($_POST['quantities']) ? $_POST['quantities'] : [];
// Validate input
if (empty($product_ids) || empty($quantities) || count($product_ids) != count($quantities)) {
echo json_encode([
'success' => false,
'message' => 'Data tidak valid.'
]);
exit;
}
// Begin transaction
$conn->begin_transaction();
try {
$success_count = 0;
$error_messages = [];
// Process each product
for ($i = 0; $i < count($product_ids); $i++) {
$id_barang = validateData($product_ids[$i]);
$quantity = intval($quantities[$i]);
if (empty($id_barang) || $quantity <= 0) {
$error_messages[] = "Data tidak valid untuk produk #$i";
continue;
}
// Check if stock is sufficient
$sql = "SELECT stok FROM barang WHERE id_barang = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id_barang);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 0) {
$error_messages[] = "Barang dengan ID $id_barang tidak ditemukan";
$stmt->close();
continue;
}
$row = $result->fetch_assoc();
$current_stock = $row['stok'];
if ($quantity > $current_stock) {
$error_messages[] = "Jumlah melebihi stok tersedia untuk produk ID $id_barang";
$stmt->close();
continue;
}
// Update stock in barang table
$new_stock = $current_stock - $quantity;
$update_sql = "UPDATE barang SET stok = ? WHERE id_barang = ?";
$update_stmt = $conn->prepare($update_sql);
$update_stmt->bind_param("is", $new_stock, $id_barang);
$update_stmt->execute();
$update_stmt->close();
$stmt->close();
// TODO: Insert data to cashier table or perform other actions as needed
// This will depend on your specific requirements for transferring to cashier
$success_count++;
}
if ($success_count == 0) {
// If no products were successfully processed, rollback and return error
$conn->rollback();
echo json_encode([
'success' => false,
'message' => 'Tidak ada produk yang berhasil dipindahkan. ' . implode('; ', $error_messages)
]);
} else {
// Commit the transaction if at least one product was successfully processed
$conn->commit();
if (count($error_messages) > 0) {
echo json_encode([
'success' => true,
'message' => "Berhasil memindahkan $success_count produk ke kasir. Beberapa produk gagal: " . implode('; ', $error_messages)
]);
} else {
echo json_encode([
'success' => true,
'message' => "Berhasil memindahkan $success_count produk ke kasir."
]);
}
}
} catch (Exception $e) {
// Rollback transaction on error
$conn->rollback();
echo json_encode([
'success' => false,
'message' => 'Terjadi kesalahan: ' . $e->getMessage()
]);
}
}
// Close connection
$conn->close();
?>

View File

@ -0,0 +1,60 @@
<?php
// Include database connection
include('../../routes/db_conn.php');
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$id_barang = $_POST['id_barang'];
$nama_barang = $_POST['nama_barang'];
$brand = $_POST['brand'];
$stok = $_POST['stok'];
$harga = $_POST['harga'];
$id_jenis = $_POST['id_jenis']; // Ambil id_jenis dari POST
$existing_image = $_POST['existing_image']; // Menyimpan nama gambar yang ada
// Cek apakah id_jenis yang dipilih valid di tabel jenis_barang
$query_jenis = "SELECT * FROM jenis_barang WHERE id_jenis = '$id_jenis'";
$result_jenis_check = mysqli_query($conn, $query_jenis);
if (mysqli_num_rows($result_jenis_check) == 0) {
echo "Error: Kategori yang dipilih tidak valid.";
exit; // Hentikan skrip jika id_jenis tidak valid
}
// Periksa apakah ada gambar yang diunggah
if (isset($_FILES['image']) && $_FILES['image']['error'] == 0) {
// Menangani unggahan file gambar
$image_name = $_FILES['image']['name'];
$image_tmp = $_FILES['image']['tmp_name'];
$image_dir = "image/" . $image_name;
// Pindahkan file yang diunggah ke direktori gambar
move_uploaded_file($image_tmp, $image_dir);
} else {
// Jika tidak ada gambar baru, gunakan gambar yang ada
$image_name = $existing_image;
}
// Membuat query UPDATE
$query = "UPDATE barang SET
nama_barang = '$nama_barang',
brand = '$brand',
stok = '$stok',
harga = '$harga',
id_jenis = '$id_jenis',
image = '$image_name'
WHERE id_barang = $id_barang";
// Debug: Tampilkan query SQL yang akan dijalankan
echo $query;
exit; // Hentikan eksekusi untuk melihat query SQL
// Eksekusi query UPDATE
if (mysqli_query($conn, $query)) {
echo "Produk berhasil diperbarui!";
header("Location: productlist.php"); // Redirect setelah pembaruan berhasil
} else {
echo "Error memperbarui produk: " . mysqli_error($conn);
}
}
?>

703
views/dashboard/index.php Normal file
View File

@ -0,0 +1,703 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
// Ambil informasi user yang sedang login
$userRole = $_SESSION['role']; // 'user' atau 'admin'
$username = $_SESSION['username']; // Menambahkan username dari session
// Jika username adalah root, tampilkan nama yang lebih presentable
$displayName = ($username === 'root') ? 'Admin' : $username;
// Cek apakah session 'user_id' ada, yang berarti pengguna sudah login
if (!isset($_SESSION['user_id'])) {
// Jika session tidak ada, arahkan pengguna ke halaman login
header("Location: /ayula-store/index.php");
exit();
}
// Database connection
include('../../routes/db_conn.php');
// Get today's date in MySQL format
$today = date('Y-m-d');
$yesterday = date('Y-m-d', strtotime('-1 day'));
$startOfWeek = date('Y-m-d', strtotime('this week monday'));
$endOfWeek = date('Y-m-d', strtotime('this week sunday'));
// Function to get employee-specific dashboard metrics
function getEmployeeDashboardData($conn) {
$today = date('Y-m-d');
$data = array();
// Today's transactions count
$query = "SELECT COUNT(*) as count FROM transaksi WHERE DATE(tanggal) = '$today'";
$result = $conn->query($query);
$data['today_transactions'] = ($result->num_rows > 0) ? $result->fetch_assoc()['count'] : 0;
// Today's items sold
$query = "SELECT SUM(total_item) as count FROM transaksi WHERE DATE(tanggal) = '$today'";
$result = $conn->query($query);
$data['today_items'] = ($result->num_rows > 0) ? $result->fetch_assoc()['count'] : 0;
if ($data['today_items'] === NULL) $data['today_items'] = 0;
// This week's transactions
$startOfWeek = date('Y-m-d', strtotime('this week monday'));
$query = "SELECT COUNT(*) as count FROM transaksi WHERE tanggal BETWEEN '$startOfWeek' AND '$today'";
$result = $conn->query($query);
$data['week_transactions'] = ($result->num_rows > 0) ? $result->fetch_assoc()['count'] : 0;
// Total products in inventory
$query = "SELECT COUNT(*) as count FROM barang";
$result = $conn->query($query);
$data['total_products'] = ($result->num_rows > 0) ? $result->fetch_assoc()['count'] : 0;
// Count low stock items (stok <= 10)
$query = "SELECT COUNT(*) as count FROM barang WHERE stok <= 10";
$result = $conn->query($query);
$data['low_stock_count'] = ($result->num_rows > 0) ? $result->fetch_assoc()['count'] : 0;
return $data;
}
// Function to get recent transactions
function getRecentTransactions($conn, $limit = 5) {
$query = "SELECT t.kode_transaksi, t.tanggal, t.total_item, t.status
FROM transaksi t
ORDER BY t.tanggal DESC
LIMIT $limit";
$result = $conn->query($query);
$transactions = array();
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$transactions[] = $row;
}
}
return $transactions;
}
// Function to get popular products - FIXED to ensure it gets the most recent data
function getPopularProducts($conn, $limit = 10) {
// Use current date to ensure we get the latest data
$today = date('Y-m-d');
$startOfWeek = date('Y-m-d', strtotime('this week monday'));
// Modified query to join with detail_transaksi directly on id_transaksi
$query = "SELECT b.kode_barang, b.nama_barang, SUM(dt.jumlah) as total_quantity
FROM detail_transaksi dt
JOIN transaksi t ON dt.id_transaksi = t.id_transaksi
JOIN barang b ON dt.id_barang = b.id_barang
WHERE t.tanggal BETWEEN '$startOfWeek' AND NOW()
GROUP BY b.id_barang
ORDER BY total_quantity DESC
LIMIT $limit";
$result = $conn->query($query);
$products = array();
if ($result && $result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$products[] = $row;
}
}
return $products;
}
// Function to get low stock items
function getLowStockItems($conn, $threshold = 10, $limit = 5) {
$query = "SELECT kode_barang, nama_barang, stok
FROM barang
WHERE stok <= $threshold
ORDER BY stok ASC
LIMIT $limit";
$result = $conn->query($query);
$lowStockItems = array();
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$lowStockItems[] = $row;
}
}
return $lowStockItems;
}
// Get dashboard data
$dashboardData = getEmployeeDashboardData($conn);
$recentTransactions = getRecentTransactions($conn);
$popularProducts = getPopularProducts($conn);
$lowStockItems = getLowStockItems($conn);
// Get user activity count (simplified)
$userActivityQuery = "SELECT COUNT(*) as count FROM transaksi WHERE tanggal > DATE_SUB(NOW(), INTERVAL 30 DAY)";
$userActivityResult = $conn->query($userActivityQuery);
$userActivity = ($userActivityResult->num_rows > 0) ? $userActivityResult->fetch_assoc()['count'] : 0;
// Recent products
$recentProductsQuery = "SELECT kode_barang, nama_barang, harga FROM barang ORDER BY created_at DESC LIMIT 5";
$recentProductsResult = $conn->query($recentProductsQuery);
$recentProducts = array();
if ($recentProductsResult->num_rows > 0) {
while($row = $recentProductsResult->fetch_assoc()) {
$recentProducts[] = $row;
}
}
// Close database connection
$conn->close();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<meta name="description" content="POS - Bootstrap Admin Template" />
<meta name="keywords"
content="admin, estimates, bootstrap, business, corporate, creative, management, minimal, modern, html5, responsive" />
<meta name="robots" content="noindex, nofollow" />
<title>Ayula Store - Dashboard</title>
<link rel="shortcut icon" type="image/x-icon" href="../../src/img/smallest-ayula.png" />
<link rel="stylesheet" href="../../bootstrap/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="../../bootstrap/assets/css/animate.css" />
<link rel="stylesheet" href="../../bootstrap/assets/css/dataTables.bootstrap4.min.css" />
<link rel="stylesheet" href="../../bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css" />
<link rel="stylesheet" href="../../bootstrap/assets/plugins/fontawesome/css/all.min.css" />
<link rel="stylesheet" href="../../bootstrap/assets/css/style.css" />
<style>
.dash-count {
border-radius: 10px;
padding: 15px;
transition: all 0.3s ease;
}
.dash-count:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.product-card {
border-radius: 10px;
transition: all 0.3s ease;
}
.product-card:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.transaction-item {
padding: 12px;
border-bottom: 1px solid #f0f0f0;
transition: all 0.2s ease;
}
.transaction-item:hover {
background-color: #f9f9f9;
}
.activity-list .activity-item {
position: relative;
padding-bottom: 16px;
padding-left: 30px;
border-left: 2px solid #e9ecef;
}
.activity-list .activity-item:before {
content: '';
position: absolute;
left: -7px;
top: 0;
background-color: #7367f0;
width: 12px;
height: 12px;
border-radius: 50%;
}
.low-stock-alert {
border-left: 4px solid #ff9f43;
}
.activity-date {
font-size: 12px;
color: #6c757d;
}
.metric-subtitle {
font-size: 13px;
color: #6c757d;
}
.welcome-message {
background: linear-gradient(to right, #1b2850, #344e9c);
color: white;
border-radius: 10px;
padding: 20px;
margin-bottom: 30px;
}
.welcome-message h4 {
font-weight: 600;
}
/* Custom 20% width column for 5 products in a row */
.col-md-20p {
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%;
}
@media (max-width: 768px) {
.col-md-20p {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
}
@media (max-width: 576px) {
.col-md-20p {
-ms-flex: 0 0 100%;
flex: 0 0 100%;
max-width: 100%;
}
}
</style>
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"></div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($displayName); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
aria-expanded="false"><i class="fa fa-ellipsis-v"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/dashboard/" class="active"><img
src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li>
<a href="/ayula-store/views/transaction/"><img src="../../bootstrap/assets/img/icons/sales1.svg"
alt="img" /><span>
POS</span></a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Produk</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php">Daftar Produk</a></li>
<!-- <li><a href="/ayula-store/views/barang/addproduct.php">Tambah Produk</a></li>
<li><a href="categorylist.html">Daftar Kategori</a></li>
<li><a href="addcategory.html">Tambah Kategori</a></li> -->
</ul>
</li>
<!-- <li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/purchase1.svg" alt="img" /><span>
Pembelian</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="purchaselist.html">Daftar Pembelian</a></li>
<li><a href="addpurchase.html">Tambah Pembelian</a></li>
<li><a href="importpurchase.html">Import Pembelian</a></li>
</ul>
</li> -->
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/time.svg" alt="img" /><span>
Laporan</span>
<span class="menu-arrow"></span></a>
<ul>
<li>
<!-- <a href="purchaseorderreport.html">Laporan Order Pembelian</a>
</li>
<li><a href="inventoryreport.html">Laporan Inventaris</a></li> -->
<li><a href="/ayula-store/views/report/sales-report/">Laporan Penjualan</a></li>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/report/popular-products/">Produk Terlaris</a></li>
<?php } ?>
<!-- <li><a href="invoicereport.html">Laporan Faktur</a></li>
<li><a href="purchasereport.html">Laporan Pembelian</a></li>
<li><a href="supplierreport.html">Laporan Pemasok</a></li>
<li><a href="customerreport.html">Laporan Pelanggan</a></li> -->
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<!-- Welcome message -->
<div class="welcome-message">
<div class="row align-items-center">
<div class="col-md-8">
<h4>Selamat Datang, <?php echo htmlspecialchars($displayName); ?>!</h4>
<p class="mb-0">Ringkasan aktivitas toko hari ini, <?php echo date('d F Y'); ?></p>
</div>
<div class="col-md-4 text-end">
<i class="fas fa-store fa-3x"></i>
</div>
</div>
</div>
<!-- Main metrics -->
<div class="row">
<div class="col-lg-3 col-sm-6 col-12 d-flex">
<div class="dash-count das1 flex-fill">
<div class="dash-counts">
<h4><?php echo number_format($dashboardData['today_transactions']); ?></h4>
<h5>Transaksi Hari Ini</h5>
<p class="metric-subtitle mb-0">
<i class="fas fa-calendar-day me-1"></i> <?php echo date('d M Y'); ?>
</p>
</div>
<div class="dash-imgs">
<i data-feather="shopping-cart"></i>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12 d-flex">
<div class="dash-count flex-fill">
<div class="dash-counts">
<h4><?php echo number_format($dashboardData['today_items']); ?></h4>
<h5>Item Terjual Hari Ini</h5>
<p class="metric-subtitle mb-0">
<i class="fas fa-box me-1"></i> Total unit terjual
</p>
</div>
<div class="dash-imgs">
<i data-feather="package"></i>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12 d-flex">
<div class="dash-count das2 flex-fill">
<div class="dash-counts">
<h4><?php echo number_format($dashboardData['week_transactions']); ?></h4>
<h5>Transaksi Minggu Ini</h5>
<p class="metric-subtitle mb-0">
<i class="fas fa-calendar-week me-1"></i> <?php echo date('d M', strtotime($startOfWeek)) . ' - ' . date('d M', strtotime($endOfWeek)); ?>
</p>
</div>
<div class="dash-imgs">
<i data-feather="file-text"></i>
</div>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12 d-flex">
<div class="dash-count das3 flex-fill">
<div class="dash-counts">
<h4><?php echo number_format($dashboardData['low_stock_count']); ?></h4>
<h5>Produk Stok Menipis</h5>
<p class="metric-subtitle mb-0">
<i class="fas fa-exclamation-triangle me-1"></i> Perlu perhatian
</p>
</div>
<div class="dash-imgs">
<i data-feather="alert-triangle"></i>
</div>
</div>
</div>
</div>
<!-- Content Cards -->
<div class="row">
<!-- Recent Transactions -->
<div class="col-lg-6 col-sm-12 col-12 d-flex">
<div class="card flex-fill">
<div class="card-header pb-0 d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Transaksi Terbaru</h5>
<a href="/ayula-store/views/report/sales-report/" class="btn btn-sm btn-primary">Lihat Semua</a>
</div>
<div class="card-body">
<?php if (count($recentTransactions) > 0): ?>
<div class="transaction-list">
<?php foreach($recentTransactions as $transaction): ?>
<div class="transaction-item d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1"><?php echo $transaction['kode_transaksi']; ?></h6>
<p class="mb-0 text-muted"><?php echo $transaction['total_item']; ?> item</p>
</div>
<div class="text-end">
<span class="badge bg-success"><?php echo $transaction['status']; ?></span>
<p class="mb-0 text-muted"><?php echo date('d M, H:i', strtotime($transaction['tanggal'])); ?></p>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="text-center py-5">
<i class="fas fa-receipt fa-3x text-muted mb-3"></i>
<h6>Belum ada transaksi hari ini</h6>
<p class="text-muted">Transaksi baru akan muncul di sini</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Low Stock Items -->
<div class="col-lg-6 col-sm-12 col-12 d-flex">
<div class="card flex-fill">
<div class="card-header pb-0 d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Stok Menipis</h5>
<a href="/ayula-store/views/barang/productlist.php" class="btn btn-sm btn-primary">Lihat Semua</a>
</div>
<div class="card-body">
<?php if (count($lowStockItems) > 0): ?>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Kode</th>
<th>Nama Produk</th>
<th>Stok</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<?php foreach($lowStockItems as $item): ?>
<tr>
<td><?php echo $item['kode_barang']; ?></td>
<td><?php echo $item['nama_barang']; ?></td>
<td><?php echo $item['stok']; ?></td>
<td>
<?php if ($item['stok'] <= 5): ?>
<span class="badge bg-danger">Kritis</span>
<?php else: ?>
<span class="badge bg-warning">Menipis</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="text-center py-5">
<i class="fas fa-box-open fa-3x text-muted mb-3"></i>
<h6>Semua Produk Memiliki Stok Memadai</h6>
<p class="text-muted">Tidak ada produk yang stoknya menipis saat ini</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Popular Products - Full Width -->
<div class="col-12 d-flex">
<div class="card flex-fill">
<div class="card-header pb-0 d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">10 Produk Terlaris Minggu Ini</h5>
<?php if ($userRole == 'admin'): ?>
<a href="/ayula-store/views/report/popular-products/" class="btn btn-sm btn-primary">Detail Lengkap</a>
<?php endif; ?>
</div>
<div class="card-body">
<?php if (count($popularProducts) > 0): ?>
<div class="row">
<?php
// Split the products into two rows for better display
$totalProducts = count($popularProducts);
$firstRowCount = min(5, $totalProducts);
$secondRowCount = $totalProducts - $firstRowCount;
?>
<!-- First row of products (top 5) -->
<?php for($i = 0; $i < $firstRowCount; $i++):
$product = $popularProducts[$i];
?>
<div class="col-md-20p mb-3">
<div class="product-card p-3 border rounded h-100">
<div class="d-flex justify-content-between align-items-start">
<div>
<h6 class="mb-1"><?php echo $product['nama_barang']; ?></h6>
<p class="mb-1 text-muted small"><?php echo $product['kode_barang']; ?></p>
</div>
<span class="badge bg-primary">#<?php echo $i + 1; ?></span>
</div>
<div class="mt-3">
<div class="d-flex justify-content-between align-items-center">
<span>Total Terjual:</span>
<span class="fw-bold"><?php echo number_format($product['total_quantity']); ?> unit</span>
</div>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: <?php echo min(100, ($product['total_quantity'] / ($popularProducts[0]['total_quantity'] ?: 1)) * 100); ?>%">
</div>
</div>
</div>
</div>
</div>
<?php endfor; ?>
<!-- Second row of products (6-10) -->
<?php if($secondRowCount > 0): ?>
<?php for($i = $firstRowCount; $i < $totalProducts; $i++):
$product = $popularProducts[$i];
?>
<div class="col-md-20p mb-3">
<div class="product-card p-3 border rounded h-100">
<div class="d-flex justify-content-between align-items-start">
<div>
<h6 class="mb-1"><?php echo $product['nama_barang']; ?></h6>
<p class="mb-1 text-muted small"><?php echo $product['kode_barang']; ?></p>
</div>
<span class="badge bg-primary">#<?php echo $i + 1; ?></span>
</div>
<div class="mt-3">
<div class="d-flex justify-content-between align-items-center">
<span>Total Terjual:</span>
<span class="fw-bold"><?php echo number_format($product['total_quantity']); ?> unit</span>
</div>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: <?php echo min(100, ($product['total_quantity'] / ($popularProducts[0]['total_quantity'] ?: 1)) * 100); ?>%">
</div>
</div>
</div>
</div>
</div>
<?php endfor; ?>
<?php endif; ?>
</div>
<?php else: ?>
<div class="text-center py-5">
<i class="fas fa-chart-line fa-3x text-muted mb-3"></i>
<h6>Belum Ada Data Penjualan</h6>
<p class="text-muted">Produk terlaris akan ditampilkan saat ada penjualan</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../../bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="../../bootstrap/assets/js/feather.min.js"></script>
<script src="../../bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="../../bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="../../bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="../../bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="../../bootstrap/assets/plugins/apexchart/apexcharts.min.js"></script>
<script src="../../bootstrap/assets/plugins/apexchart/chart-data.js"></script>
<script src="../../bootstrap/assets/js/script.js"></script>
<script>
// Hide loader when page is fully loaded
$(window).on('load', function() {
setTimeout(function() {
$("#global-loader").fadeOut('slow');
}, 100);
});
// Initialize any DataTables
$(document).ready(function() {
if ($.fn.DataTable.isDataTable('.datatable') === false) {
$('.datatable').DataTable({
language: {
search: "Cari:",
lengthMenu: "Tampilkan _MENU_ data",
info: "Menampilkan _START_ sampai _END_ dari _TOTAL_ data",
infoEmpty: "Menampilkan 0 sampai 0 dari 0 data",
infoFiltered: "(disaring dari _MAX_ total data)",
zeroRecords: "Tidak ada data yang cocok",
paginate: {
first: "Pertama",
last: "Terakhir",
next: "Selanjutnya",
previous: "Sebelumnya"
}
}
});
}
});
</script>
</body>
</html>

118
views/forgot-password.php Normal file
View File

@ -0,0 +1,118 @@
<?php
session_start();
include('../routes/db_conn.php'); // Menyertakan koneksi database
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Menangkap data yang dikirimkan dari form
$username = mysqli_real_escape_string($conn, $_POST['username']);
$new_password = mysqli_real_escape_string($conn, $_POST['new_password']);
$confirm_password = mysqli_real_escape_string($conn, $_POST['confirm_password']);
// Validasi apakah password baru dan konfirmasi password cocok
if ($new_password !== $confirm_password) {
$error_message = "Passwords do not match!";
} else {
// Hash password untuk keamanan
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
// Query untuk mengecek apakah username ada di database
$query = "SELECT * FROM kasir WHERE username = '$username' LIMIT 1";
$result = mysqli_query($conn, $query);
// Jika username ditemukan
if (mysqli_num_rows($result) > 0) {
// Query untuk memperbarui password di database
$query = "UPDATE kasir SET password = '$hashed_password' WHERE username = '$username'";
if (mysqli_query($conn, $query)) {
// Jika berhasil, alihkan ke halaman login
$_SESSION['username'] = $username;
header("Location: index.php");
exit();
} else {
$error_message = "Gagal memperbarui password!";
}
} else {
$error_message = "Username tidak ditemukan!";
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<meta name="description" content="POS - Bootstrap Admin Template" />
<meta name="keywords" content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects" />
<meta name="author" content="Dreamguys - Bootstrap Admin Template" />
<meta name="robots" content="noindex, nofollow" />
<title>Reset Password - Ayula Store</title>
<link rel="shortcut icon" type="image/x-icon" href="../src/img/smallest-ayula.png" />
<link rel="stylesheet" href="../bootstrap/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/plugins/fontawesome/css/all.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/css/style.css" />
</head>
<body class="account-page">
<div class="main-wrapper">
<div class="account-content">
<div class="login-wrapper">
<div class="login-content">
<div class="login-userset">
<div class="login-userheading">
<h3>Reset Password</h3>
<h4>Enter your new password below.</h4>
</div>
<form method="POST" action="forgot-password.php">
<div class="form-login">
<label>Username</label>
<div class="form-addons">
<input type="text" name="username" placeholder="Enter your username" required />
<img src="../bootstrap/assets/img/icons/users1.svg" alt="img" />
</div>
</div>
<div class="form-login">
<label>New Password</label>
<div class="pass-group">
<input type="password" name="new_password" class="pass-input" placeholder="Enter your new password" required />
<span class="fas toggle-password fa-eye-slash"></span>
</div>
</div>
<div class="form-login">
<label>Confirm New Password</label>
<div class="pass-group">
<input type="password" name="confirm_password" class="pass-input" placeholder="Confirm your new password" required />
<span class="fas toggle-password fa-eye-slash"></span>
</div>
</div>
<?php if (isset($error_message)) { ?>
<div class="error-message">
<p style="color: red;"><?php echo $error_message; ?></p>
</div>
<?php } ?>
<div class="form-login">
<button type="submit" class="btn btn-login">Reset Password</button>
</div>
<div class="signinform text-center">
<h4>Remembered? <a href="index.php" class="hover-a">Sign In</a></h4>
</div>
</form>
</div>
</div>
<div class="login-img">
<img src="../bootstrap/assets/img/login.jpg" alt="img" />
</div>
</div>
</div>
</div>
<script src="../bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="../bootstrap/assets/js/feather.min.js"></script>
<script src="../bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="../bootstrap/assets/js/script.js"></script>
</body>
</html>

278
views/index.php Normal file
View File

@ -0,0 +1,278 @@
<?php
session_start();
include('../routes/db_conn.php'); // Menyertakan koneksi database
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Menangkap data dari form login
$username = mysqli_real_escape_string($conn, $_POST['username']);
$password = $_POST['password']; // Password yang dimasukkan oleh pengguna
// Query untuk mencari pengguna berdasarkan username
$query = "SELECT * FROM kasir WHERE username = '$username'";
$result = mysqli_query($conn, $query);
// Mengecek apakah user ditemukan
if (mysqli_num_rows($result) > 0) {
$user = mysqli_fetch_assoc($result);
// Verifikasi password menggunakan password_verify()
if (password_verify($password, $user['password'])) {
// Jika password valid, login berhasil
$_SESSION['user_id'] = $user['id_kasir'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
// Jika role adalah admin, tampilkan modal pilihan
if ($user['role'] == 'admin') {
// Set flag untuk menampilkan modal
$showRoleModal = true;
} else {
// Jika bukan admin, redirect langsung ke dashboard
header("Location: /ayula-store/views/dashboard/");
exit();
}
} else {
// Jika password salah
$error_message = "Password salah!";
}
} else {
// Jika username tidak ditemukan
$error_message = "Username tidak ditemukan!";
}
}
// Proses pilihan role dari modal
if (isset($_POST['role_choice'])) {
if ($_POST['role_choice'] == 'gudang') {
header("Location: /ayula-store/views/reporttt/report.php");
exit();
} else if ($_POST['role_choice'] == 'kasir') {
header("Location: /ayula-store/views/dashboard/");
exit();
}
}
// Proses logout jika tombol close ditekan
if (isset($_POST['logout'])) {
// Hapus semua data session
session_unset();
session_destroy();
// Redirect ke halaman login
header("Location: index.php");
exit();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<meta name="description" content="POS - Bootstrap Admin Template" />
<meta name="keywords" content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects" />
<meta name="author" content="Dreamguys - Bootstrap Admin Template" />
<meta name="robots" content="noindex, nofollow" />
<title>Login - Ayula Store</title>
<link rel="shortcut icon" type="image/x-icon" href="../src/img/smallest-ayula.png" />
<link rel="stylesheet" href="../bootstrap/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/plugins/fontawesome/css/all.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/css/style.css" />
<style>
.role-btn {
padding: 20px;
border-radius: 10px;
font-size: 18px;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 150px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border: none;
margin-bottom: 15px;
width: 100%;
}
.role-btn:hover {
transform: translateY(-5px);
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
}
.role-btn i {
font-size: 40px;
margin-bottom: 15px;
}
.gudang-btn {
background-color: #ff9f43;
color: white;
}
.gudang-btn:hover {
background-color: #ffb63f;
color: white;
}
.kasir-btn {
background-color: #1b2850;
color: white;
}
.kasir-btn:hover {
background-color: #344e9c;
color: white;
}
.modal-title {
font-weight: 700;
color: #333;
}
.modal-header {
border-bottom: 2px solid #f0f0f0;
padding: 20px 25px;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-body {
padding: 25px;
}
.welcome-text {
font-size: 16px;
margin-bottom: 25px;
text-align: center;
}
.role-container {
padding: 0 15px;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
color: #888;
cursor: pointer;
transition: color 0.3s ease;
padding: 0;
margin-left: auto;
}
.close-btn:hover {
color: #ff6b6b;
}
.close-btn:focus {
outline: none;
}
</style>
</head>
<body class="account-page">
<div class="main-wrapper">
<div class="account-content">
<div class="login-wrapper">
<div class="login-content">
<div class="login-userset">
<div class="login-userheading">
<h3>Masuk</h3>
<h4>Silakan masuk ke akun Anda</h4>
</div>
<form method="POST" action="index.php">
<div class="form-login">
<label>Username</label>
<div class="form-addons">
<input type="text" name="username" placeholder="Enter your username" required />
<img src="../bootstrap/assets/img/icons/users1.svg" alt="img" />
</div>
</div>
<div class="form-login">
<label>Password</label>
<div class="pass-group">
<input type="password" name="password" class="pass-input" placeholder="Enter your password" required />
<span class="fas toggle-password fa-eye-slash"></span>
</div>
</div>
<div class="form-login">
<div class="alreadyuser">
<h4>
<a href="forgot-password.php" class="hover-a">Lupa Password?</a>
</h4>
</div>
</div>
<?php if (isset($error_message)) { ?>
<div class="error-message">
<p style="color: red;"><?php echo $error_message; ?></p>
</div>
<?php } ?>
<div class="form-login">
<button type="submit" class="btn btn-login">Masuk</button>
</div>
<!-- <div class="signinform text-center">
<h4>Don't have an account? <a href="register.php" class="hover-a">Sign Up</a></h4>
</div> -->
</form>
</div>
</div>
<div class="login-img">
<img src="../bootstrap/assets/img/login.jpg" alt="img" />
</div>
</div>
</div>
</div>
<!-- Modal Pilihan Role untuk Admin -->
<div class="modal fade" id="roleModal" tabindex="-1" role="dialog" aria-labelledby="roleModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="roleModalLabel">Pilih Akses</h5>
<form method="POST" action="" id="logoutForm">
<input type="hidden" name="logout" value="1">
<button type="submit" class="close-btn" title="Keluar">
<i class="fas fa-times"></i>
</button>
</form>
</div>
<div class="modal-body">
<p class="welcome-text">Selamat datang, <strong><?php echo isset($_SESSION['username']) ? $_SESSION['username'] : ''; ?></strong>! <br>Silakan pilih akses yang ingin Anda gunakan:</p>
<form method="POST" action="">
<div class="row role-container">
<div class="col-md-6">
<button type="submit" name="role_choice" value="gudang" class="role-btn gudang-btn">
<i class="fas fa-warehouse"></i>
<span>Gudang</span>
</button>
</div>
<div class="col-md-6">
<button type="submit" name="role_choice" value="kasir" class="role-btn kasir-btn">
<i class="fas fa-cash-register"></i>
<span>Kasir</span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<script src="../bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="../bootstrap/assets/js/feather.min.js"></script>
<script src="../bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="../bootstrap/assets/js/script.js"></script>
<?php if (isset($showRoleModal) && $showRoleModal): ?>
<script>
$(document).ready(function() {
$('#roleModal').modal('show');
});
</script>
<?php endif; ?>
</body>
</html>

24
views/logout.php Normal file
View File

@ -0,0 +1,24 @@
<?php
// Start the session
session_start();
// Unset all session variables
$_SESSION = array();
// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// Finally, destroy the session
session_destroy();
// Redirect to login page
header("Location: /ayula-store/views/index.php");
exit;
?>

110
views/register.php Normal file
View File

@ -0,0 +1,110 @@
<?php
session_start();
include('../routes/db_conn.php'); // Menyertakan koneksi database
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Menangkap data yang dikirimkan dari form
$username = mysqli_real_escape_string($conn, $_POST['username']);
$password = mysqli_real_escape_string($conn, $_POST['password']);
$phone = mysqli_real_escape_string($conn, $_POST['phone']);
// Hash password untuk keamanan
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Query untuk mengecek apakah username sudah ada di database
$query = "SELECT * FROM kasir WHERE username = '$username' LIMIT 1";
$result = mysqli_query($conn, $query);
// Jika username sudah ada
if (mysqli_num_rows($result) > 0) {
$error_message = "Username sudah terdaftar!";
} else {
// Query untuk memasukkan data baru ke dalam database
$query = "INSERT INTO kasir (username, password, phone, role) VALUES ('$username', '$hashed_password', '$phone', 'user')";
if (mysqli_query($conn, $query)) {
// Jika berhasil, alihkan ke halaman login
$_SESSION['username'] = $username;
header("Location: index.php");
exit();
} else {
$error_message = "Gagal melakukan registrasi!";
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<meta name="description" content="POS - Bootstrap Admin Template" />
<meta name="keywords" content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects" />
<meta name="author" content="Dreamguys - Bootstrap Admin Template" />
<meta name="robots" content="noindex, nofollow" />
<title>Register - Ayula Store</title>
<link rel="shortcut icon" type="image/x-icon" href="../src/img/smallest-ayula.png" />
<link rel="stylesheet" href="../bootstrap/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/plugins/fontawesome/css/all.min.css" />
<link rel="stylesheet" href="../bootstrap/assets/css/style.css" />
</head>
<body class="account-page">
<div class="main-wrapper">
<div class="account-content">
<div class="login-wrapper">
<div class="login-content">
<div class="login-userset">
<div class="login-userheading">
<h3>Create an Account</h3>
<h4>Continue where you left off</h4>
</div>
<form method="POST" action="register.php">
<div class="form-login">
<label>Username</label>
<div class="form-addons">
<input type="text" name="username" placeholder="Enter your username" required />
<img src="../bootstrap/assets/img/icons/users1.svg" alt="img" />
</div>
</div>
<div class="form-login">
<label>Password</label>
<div class="pass-group">
<input type="password" name="password" class="pass-input" placeholder="Enter your password" required />
<span class="fas toggle-password fa-eye-slash"></span>
</div>
</div>
<div class="form-login">
<label>Phone number</label>
<div class="form-addons">
<input type="text" name="phone" placeholder="Enter your phone number" required />
<img src="../bootstrap/assets/img/icons/telephone.png" alt="img" />
</div>
</div>
<?php if (isset($error_message)) { ?>
<div class="error-message">
<p style="color: red;"><?php echo $error_message; ?></p>
</div>
<?php } ?>
<div class="form-login">
<button type="submit" class="btn btn-login">Sign Up</button>
</div>
<div class="signinform text-center">
<h4>Already a user? <a href="index.php" class="hover-a">Sign In</a></h4>
</div>
</form>
</div>
</div>
<div class="login-img">
<img src="../bootstrap/assets/img/login.jpg" alt="img" />
</div>
</div>
</div>
</div>
<script src="../bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="../bootstrap/assets/js/feather.min.js"></script>
<script src="../bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="../bootstrap/assets/js/script.js"></script>
</body>
</html>

View File

@ -0,0 +1,384 @@
<?php
// Start session to get user information
session_start();
// Include database connection if needed for user validation
include('../../routes/db_conn.php');
// Get user role and name if available
$userRole = isset($_SESSION['role']) ? $_SESSION['role'] : 'guest';
$username = isset($_SESSION['username']) ? $_SESSION['username'] : 'Unknown User';
$isAdmin = ($userRole === 'admin');
// WhatsApp number (with country code format for Indonesia)
$waNumber = "6287857242169"; // 62 + 87704632355 (removing the leading 0)
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="description" content="Ayula Store - Laporkan Masalah">
<meta name="keywords" content="admin, reports, issues, support, ayula store">
<meta name="author" content="Ayula Store Developer">
<meta name="robots" content="noindex, nofollow">
<title>Ayula Store POS - Laporkan Masalah</title>
<link rel="shortcut icon" type="image/x-icon" href="../../src/img/smallest-ayula.png">
<link rel="stylesheet" href="../../bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="../../bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="../../bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="../../bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="../../bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="../../bootstrap/assets/css/style.css">
<style>
.report-card {
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.report-header {
background-color: #f8f9fa;
padding: 20px;
border-bottom: 1px solid #e9ecef;
border-radius: 10px 10px 0 0;
}
.report-body {
padding: 30px;
}
.wa-button {
background-color: #25D366;
color: white;
border: none;
border-radius: 5px;
padding: 12px 24px;
font-weight: 600;
transition: all 0.3s ease;
}
.wa-button:hover {
background-color: #128C7E;
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.wa-icon {
margin-right: 8px;
}
.issue-type-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}
.issue-type-btn {
flex: 1 0 calc(33.333% - 10px);
min-width: 150px;
padding: 15px;
border: 1px solid #dee2e6;
border-radius: 5px;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
}
.issue-type-btn:hover {
background-color: #f8f9fa;
border-color: #ced4da;
}
.issue-type-btn.active {
background-color: #ff9f43;
color: white;
border-color: #ff9f43;
}
.issue-type-btn i {
display: block;
font-size: 24px;
margin-bottom: 10px;
}
@media (max-width: 768px) {
.issue-type-btn {
flex: 1 0 calc(50% - 10px);
}
}
@media (max-width: 576px) {
.issue-type-btn {
flex: 1 0 100%;
}
}
</style>
</head>
<body class="<?php echo $isAdmin ? 'admin' : 'employee'; ?>">
<div id="global-loader">
<div class="whirly-loader"></div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img"><img src="../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($displayName); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
aria-expanded="false"><i class="fa fa-ellipsis-v"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg" class="me-2" alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li class="active">
<a href="/ayula-store/views/reporttt/report.php"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Barang</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php" >Daftar Barang</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Barang</a></li>
</ul>
</li>
<li >
<a href="/ayula-store/views/barang/topsis_restock_view.php"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
Analisa Barang</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Laporkan Masalah</h4>
<h6>Laporkan masalah atau saran untuk pengembangan sistem</h6>
</div>
</div>
<div class="card report-card">
<div class="card-header report-header">
<h5 class="card-title">Hubungi Developer via WhatsApp</h5>
</div>
<div class="card-body report-body">
<div class="alert alert-info mb-4">
<i class="fas fa-info-circle me-2"></i>
Isi formulir di bawah ini untuk melaporkan masalah. Pesan akan dikirim langsung ke developer melalui WhatsApp.
</div>
<form id="issue-report-form">
<div class="mb-4">
<label class="form-label">Jenis Masalah</label>
<div class="issue-type-container">
<div class="issue-type-btn" data-type="Bug/Error">
<i class="fas fa-bug"></i>
<span>Bug/Error</span>
</div>
<div class="issue-type-btn" data-type="Fitur Tidak Berfungsi">
<i class="fas fa-exclamation-triangle"></i>
<span>Fitur Tidak Berfungsi</span>
</div>
<div class="issue-type-btn" data-type="Permintaan Fitur">
<i class="fas fa-lightbulb"></i>
<span>Permintaan Fitur</span>
</div>
<div class="issue-type-btn" data-type="Optimasi Kinerja">
<i class="fas fa-tachometer-alt"></i>
<span>Optimasi Kinerja</span>
</div>
<div class="issue-type-btn" data-type="UI/UX">
<i class="fas fa-palette"></i>
<span>UI/UX</span>
</div>
<div class="issue-type-btn" data-type="Lainnya">
<i class="fas fa-question-circle"></i>
<span>Lainnya</span>
</div>
</div>
<input type="hidden" id="issue-type" name="issue-type" value="">
</div>
<div class="mb-4">
<label for="issue-location" class="form-label">Lokasi Masalah</label>
<select class="form-select" id="issue-location" name="issue-location">
<option value="">Pilih Halaman/Fitur...</option>
<option value="Login">Login</option>
<option value="Dashboard">Dashboard</option>
<option value="POS/Transaksi">Barang</option>
<option value="Produk">Analisa barang</option>
<option value="Pengguna">Pengguna</option>
<option value="Lainnya">Lainnya</option>
</select>
</div>
<div class="mb-4">
<label for="issue-priority" class="form-label">Prioritas</label>
<select class="form-select" id="issue-priority" name="issue-priority">
<option value="Rendah">Rendah - Tidak mengganggu operasi utama</option>
<option value="Sedang" selected>Sedang - Mengganggu tapi ada solusi</option>
<option value="Tinggi">Tinggi - Menghambat pekerjaan</option>
<option value="Kritis">Kritis - Sistem tidak dapat digunakan</option>
</select>
</div>
<div class="mb-4">
<label for="issue-description" class="form-label">Deskripsi Masalah</label>
<textarea class="form-control" id="issue-description" name="issue-description" rows="5" placeholder="Jelaskan masalah dengan detail. Berikan langkah-langkah untuk mereproduksi masalah (jika ada)..."></textarea>
</div>
<div class="mb-4">
<label for="issue-contact" class="form-label">Kontak Anda (Opsional)</label>
<input type="text" class="form-control" id="issue-contact" name="issue-contact" placeholder="Nomor telepon atau email untuk follow-up" value="<?php echo htmlspecialchars($username); ?>">
</div>
<div class="text-center mt-5">
<button type="button" id="send-wa-btn" class="btn wa-button btn-lg">
<i class="fab fa-whatsapp wa-icon"></i>
Kirim Laporan via WhatsApp
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="../../bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="../../bootstrap/assets/js/feather.min.js"></script>
<script src="../../bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="../../bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="../../bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="../../bootstrap/assets/js/script.js"></script>
<script>
$(document).ready(function() {
// Handle issue type selection with animation
$('.issue-type-btn').on('click', function() {
// Remove active class from all buttons with animation
$('.issue-type-btn').removeClass('active').fadeOut(200).fadeIn(200);
$(this).addClass('active');
// Set the value in the hidden input
$('#issue-type').val($(this).data('type'));
});
// WhatsApp send button functionality with custom message
$('#send-wa-btn').on('click', function() {
var issueType = $('#issue-type').val() || 'Tidak ditentukan';
var issueLocation = $('#issue-location').val() || 'Tidak ditentukan';
var issuePriority = $('#issue-priority').val() || 'Sedang';
var issueDescription = $('#issue-description').val() || 'Tidak ada deskripsi';
var issueContact = $('#issue-contact').val() || '<?php echo htmlspecialchars($username); ?>';
// Validation check before sending
if (!issueType) {
alert('Mohon pilih jenis masalah terlebih dahulu.');
return;
}
if (!issueDescription) {
alert('Mohon isi deskripsi masalah terlebih dahulu.');
return;
}
var message = "🔴 *LAPORAN MASALAH AYULA STORE* 🔴\n\n" +
"*User:* <?php echo htmlspecialchars($username); ?> (<?php echo htmlspecialchars($userRole); ?>)\n" +
"*Jenis Masalah:* " + issueType + "\n" +
"*Lokasi:* " + issueLocation + "\n" +
"*Prioritas:* " + issuePriority + "\n\n" +
"*Deskripsi:*\n" + issueDescription + "\n\n" +
"*Kontak:* " + issueContact;
var encodedMessage = encodeURIComponent(message);
var waLink = "https://wa.me/<?php echo $waNumber; ?>?text=" + encodedMessage;
window.open(waLink, '_blank');
});
});
</script>
</body>
</html>

View File

@ -0,0 +1,949 @@
<?php
// session_start();
// Include the configuration file with database functions and user permissions
include('popular-products-config.php');
// Assuming the user role is stored in session after login
$userRole = getUserRole();
$username = getUsername();
// Get appropriate date presets based on user role
$isAdmin = isAdmin();
$datePresets = getDatePresets($isAdmin);
// Handle reset request
if (isset($_GET['reset']) && $_GET['reset'] == 1) {
// If reset is requested, redirect to same page with default dates
header("Location: index.php");
exit;
}
// If user is not admin, limit historical data access
if (!$isAdmin && empty($_GET['preset']) && empty($_GET['start_date'])) {
// Default non-admin users to current month if no dates specified
$_GET['preset'] = 'this_month';
}
// Check if preset is selected
$activePreset = isset($_GET['preset']) && !empty($_GET['preset']) ? $_GET['preset'] : '';
// Apply date ranges
$startDate = getStartDate($datePresets, $activePreset);
$endDate = getEndDate($datePresets, $activePreset);
// Ensure start date is not after end date
if (strtotime($startDate) > strtotime($endDate)) {
$temp = $startDate;
$startDate = $endDate;
$endDate = $temp;
}
// Default limit for top products
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 20;
if ($limit < 5) $limit = 5;
if ($limit > 100) $limit = 100;
// Default sort type (quantity or revenue)
$sortBy = isset($_GET['sort_by']) ? $_GET['sort_by'] : 'quantity';
if (!in_array($sortBy, ['quantity', 'revenue'])) {
$sortBy = 'quantity';
}
// Get the popular products based on user role
$queryData = getPopularProducts($startDate, $endDate, $limit, $sortBy, $isAdmin);
$result = $queryData['result'];
$summary = $queryData['summary'];
$categories = $queryData['categories'];
// Handle PDF export request if admin
if (isset($_POST['export_pdf']) || (isset($_GET['export']) && $_GET['export'] == 'pdf')) {
exportToPDF($startDate, $endDate, $limit, $sortBy, $isAdmin);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="description" content="POS - Bootstrap Admin Template">
<meta name="keywords" content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects">
<meta name="author" content="Dreamguys - Bootstrap Admin Template">
<meta name="robots" content="noindex, nofollow">
<title>Ayula Store POS - Produk Terlaris</title>
<link rel="shortcut icon" type="image/x-icon" href="../../../src/img/smallest-ayula.png">
<link rel="stylesheet" href="../../../bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/css/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="../../../bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/css/style.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="../salesreport.css">
<link rel="stylesheet" href="popular-products.css">
<!-- Chart.js for visualizations -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.css">
</head>
<body class="<?php echo $isAdmin ? 'admin' : 'employee'; ?>">
<!-- Loading Overlay -->
<div id="loading-overlay">
<div class="loading-spinner"></div>
<div class="loading-text">Memuat data produk terlaris...</div>
</div>
<div id="global-loader">
<div class="whirly-loader"></div>
</div>
<!-- Header (similar to sales report) -->
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img"><img src="../../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img"><img src="../../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
<div class="profilesets">
<h6><?php echo $isAdmin ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($username); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../../views/logout.php"><img
src="../../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
aria-expanded="false"><i class="fa fa-ellipsis-v"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</div>
<!-- Sidebar (similar to sales report) -->
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/dashboard/"><img src="../../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li>
<a href="/ayula-store/views/transaction/"><img src="../../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
POS</span></a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Produk</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php">Daftar Produk</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Produk</a></li>
<li><a href="categorylist.html">Daftar Kategori</a></li>
<li><a href="addcategory.html">Tambah Kategori</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../../bootstrap/assets/img/icons/purchase1.svg" alt="img" /><span>
Pembelian</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="purchaselist.html">Daftar Pembelian</a></li>
<li><a href="addpurchase.html">Tambah Pembelian</a></li>
<li><a href="importpurchase.html">Import Pembelian</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../../bootstrap/assets/img/icons/time.svg" alt="img" /><span>
Laporan</span>
<span class="menu-arrow"></span></a>
<ul>
<li>
<a href="purchaseorderreport.html">Laporan Order Pembelian</a>
</li>
<li><a href="inventoryreport.html">Laporan Inventaris</a></li>
<li><a href="/ayula-store/views/report/sales-report/">Laporan Penjualan</a></li>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/report/popular-products/">Produk Terlaris</a></li>
<?php } ?>
<li><a href="invoicereport.html">Laporan Faktur</a></li>
<li><a href="purchasereport.html">Laporan Pembelian</a></li>
<li><a href="supplierreport.html">Laporan Pemasok</a></li>
<li><a href="customerreport.html">Laporan Pelanggan</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Laporan Produk Terlaris</h4>
<h6>Lihat dan analisis produk paling populer</h6>
<?php if (!$isAdmin): ?>
<div class="alert alert-info mt-2 employee-warning">
<small><i class="fa fa-info-circle me-1"></i> Anda melihat laporan ini dengan akses karyawan. Beberapa data mungkin dibatasi.</small>
</div>
<?php endif; ?>
</div>
</div>
<!-- Dashboard stat widgets -->
<div class="row">
<div class="col-lg-4 col-sm-6 col-12 d-flex">
<div class="dash-widget flex-fill">
<div class="dash-widgetimg">
<span class="dash-widget-icon"><i class="fa fa-box"></i></span>
</div>
<div class="dash-widgetcontent">
<h5>Total <span class="counters"><?php echo number_format($summary['total_products']); ?></span></h5>
<h6>Produk Terjual</h6>
</div>
</div>
</div>
<div class="col-lg-4 col-sm-6 col-12 d-flex">
<div class="dash-widget flex-fill">
<div class="dash-widgetimg">
<span class="dash-widget-icon"><i class="fa fa-shopping-basket"></i></span>
</div>
<div class="dash-widgetcontent">
<h5>Total <span class="counters"><?php echo number_format($summary['total_items_sold']); ?></span></h5>
<h6>Item Terjual</h6>
</div>
</div>
</div>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<div class="col-lg-4 col-sm-6 col-12 d-flex">
<div class="dash-widget flex-fill">
<div class="dash-widgetimg">
<span class="dash-widget-icon"><i class="fa fa-money-bill-alt"></i></span>
</div>
<div class="dash-widgetcontent">
<h5>Rp. <span class="counters"><?php echo number_format($summary['total_revenue']); ?></span></h5>
<h6>Total Pendapatan</h6>
</div>
</div>
</div>
<?php else: ?>
<!-- Employee placeholder for layout consistency -->
<div class="col-lg-4 col-sm-6 col-12 d-flex">
<div class="dash-widget flex-fill">
<div class="dash-widgetimg">
<span class="dash-widget-icon"><i class="fa fa-chart-line"></i></span>
</div>
<div class="dash-widgetcontent">
<h5>Laporan Aktivitas</h5>
<h6>Hubungi admin untuk detail keuangan</h6>
</div>
</div>
</div>
<?php endif; ?>
</div>
<!-- Enhanced Date Filter with Presets -->
<!-- Enhanced Date Filter with Presets -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Filter Tanggal</h5>
</div>
<div class="card-body">
<form action="" method="GET" id="date-filter-form">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="preset">Rentang Waktu:</label>
<select name="preset" id="preset" class="form-control">
<?php if ($isAdmin): ?>
<option value="">Kustom</option>
<?php endif; ?>
<?php
// Buat array terjemahan untuk label preset yang lebih familiar
$presetLabels = [
'today' => 'Hari Ini',
'yesterday' => 'Kemarin',
'this_week' => 'Minggu Ini',
'last_week' => 'Minggu Lalu',
'this_month' => 'Bulan Ini',
'last_month' => 'Bulan Lalu',
// 'last_90_days' => '3 Bulan Terakhir',
'this_year' => 'Tahun Ini',
'all_time' => 'Seluruh Waktu'
];
foreach ($datePresets as $key => $preset):
$label = isset($presetLabels[$key]) ? $presetLabels[$key] : $preset['label'];
?>
<option value="<?php echo $key; ?>" <?php echo ($activePreset === $key) ? 'selected' : ''; ?>>
<?php echo $label; ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<?php if ($isAdmin): ?>
<!-- Custom Date Inputs - Admin Only -->
<div class="col-md-8">
<div class="row custom-date-inputs" id="custom-date-inputs" <?php echo !empty($activePreset) ? 'style="display:none;"' : ''; ?>>
<div class="col-md-4">
<div class="form-group">
<label for="start_date">Dari Tanggal:</label>
<input type="date" id="start_date" name="start_date" class="form-control"
value="<?php echo $startDate; ?>">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="end_date">Sampai Tanggal:</label>
<input type="date" id="end_date" name="end_date" class="form-control"
value="<?php echo $endDate; ?>">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>&nbsp;</label>
<button type="submit" class="btn btn-primary w-100" id="search-button">
<i class="fas fa-search"></i> Cari
</button>
</div>
</div>
</div>
</div>
<?php else: ?>
<!-- For employees - simpler view -->
<div class="col-md-4">
<div class="form-group">
<label for="limit">Jumlah Produk:</label>
<select name="limit" id="limit" class="form-control">
<option value="5" <?php echo ($limit == 5) ? 'selected' : ''; ?>>Top 5</option>
<option value="10" <?php echo ($limit == 10) ? 'selected' : ''; ?>>Top 10</option>
<option value="20" <?php echo ($limit == 20) ? 'selected' : ''; ?>>Top 20</option>
<option value="50" <?php echo ($limit == 50) ? 'selected' : ''; ?>>Top 50</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>&nbsp;</label>
<button type="submit" class="btn btn-primary" id="search-button">
<i class="fas fa-search"></i> Terapkan Filter
</button>
</div>
</div>
<?php endif; ?>
<!-- Sorting options (for both admin and employee) -->
<div class="col-md-4 <?php echo (!$isAdmin) ? 'd-none d-md-block' : ''; ?>">
<div class="form-group">
<label for="sort_by">Urutkan Berdasarkan:</label>
<select name="sort_by" id="sort_by" class="form-control">
<option value="quantity" <?php echo ($sortBy == 'quantity') ? 'selected' : ''; ?>>Jumlah Terjual</option>
<option value="revenue" <?php echo ($sortBy == 'revenue') ? 'selected' : ''; ?>>Total Pendapatan</option>
</select>
</div>
</div>
<?php if ($isAdmin): ?>
<div class="col-md-4">
<div class="form-group">
<label for="limit">Jumlah Produk:</label>
<select name="limit" id="limit" class="form-control">
<option value="5" <?php echo ($limit == 5) ? 'selected' : ''; ?>>Top 5</option>
<option value="10" <?php echo ($limit == 10) ? 'selected' : ''; ?>>Top 10</option>
<option value="20" <?php echo ($limit == 20) ? 'selected' : ''; ?>>Top 20</option>
<option value="50" <?php echo ($limit == 50) ? 'selected' : ''; ?>>Top 50</option>
<option value="100" <?php echo ($limit == 100) ? 'selected' : ''; ?>>Top 100</option>
</select>
</div>
</div>
<?php endif; ?>
</div>
<div class="row mt-3">
<div class="col-12 d-flex justify-content-end">
<a href="?reset=1" class="btn btn-secondary me-2">
<i class="fas fa-redo"></i> Atur Ulang
</a>
<!-- <?php if ($isAdmin || canAccessFeature('print_report')): ?>
<a href="#" class="btn btn-info me-2 print-report">
<i class="fas fa-print"></i> Cetak
</a>
<?php else: ?>
<a href="#" class="btn btn-info me-2 employee-print request-only"
data-action="print_attempt">
<i class="fas fa-print"></i> Cetak
</a>
<?php endif; ?>
<?php if ($isAdmin || canAccessFeature('export_excel') || canAccessFeature('export_pdf')): ?>
<div class="btn-group">
<button type="button" class="btn btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-download"></i> Ekspor
</button>
<ul class="dropdown-menu">
<?php if ($isAdmin || canAccessFeature('export_excel')): ?>
<li><a class="dropdown-item excel-export" href="#"><i class="fas fa-file-excel me-2"></i> Excel</a></li>
<?php endif; ?>
<?php if ($isAdmin || canAccessFeature('export_pdf')): ?>
<li><a class="dropdown-item pdf-export" href="#"><i class="fas fa-file-pdf me-2"></i> PDF</a></li>
<?php endif; ?>
</ul>
</div>
<?php else: ?>
<!-- Employee-limited export (with logging) -->
<a href="#" class="btn btn-success request-only employee-export"
data-action="export_attempt">
<i class="fas fa-download"></i> Export
</a>
<?php endif; ?> -->
</div>
</div>
</form>
</div>
</div>
<!-- Current Date Range Display -->
<div class="alert alert-info mb-4">
<strong>Rentang Tanggal:</strong> <?php echo date('d M Y', strtotime($startDate)); ?> sampai
<?php echo date('d M Y', strtotime($endDate)); ?>
<?php if (!empty($activePreset)): ?>
<span class="badge bg-primary ms-2"><?php echo $datePresets[$activePreset]['label']; ?></span>
<?php endif; ?>
<div class="small mt-1">
<?php
$daysDiff = (strtotime($endDate) - strtotime($startDate)) / (60 * 60 * 24) + 1;
echo "Menampilkan data untuk " . number_format($daysDiff) . " hari";
?>
</div>
</div>
<!-- Category Distribution Charts (for both admin and employee) -->
<?php if ($queryData['has_data'] && $categories && mysqli_num_rows($categories) > 0): ?>
<div class="row">
<div class="col-md-6">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Kategori Produk Terlaris</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="categoryPieChart" height="300"></canvas>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Distribusi Penjualan per Kategori</h5>
</div>
<div class="card-body">
<div class="chart-container">
<canvas id="categoryBarChart" height="300"></canvas>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<!-- Top Products Section -->
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<?php
echo "Top " . $limit . " Produk";
echo ($sortBy == 'revenue') ? " (Berdasarkan Pendapatan)" : " (Berdasarkan Kuantitas)";
?>
</h5>
<div class="view-toggle btn-group">
<button type="button" class="btn btn-sm btn-outline-secondary active" data-view="grid">
<i class="fas fa-th-large"></i>
</button>
<button type="button" class="btn btn-sm btn-outline-secondary" data-view="table">
<i class="fas fa-list"></i>
</button>
</div>
</div>
<div class="card-body">
<?php if (!$queryData['has_data']): ?>
<!-- Tampilan No Data Found (Tidak ada data) -->
<div class="no-data-container">
<div class="no-data-icon">
<i class="fas fa-box-open"></i>
</div>
<h4 class="no-data-message">Tidak ada data penjualan produk untuk periode yang dipilih</h4>
<p class="no-data-help">
<?php if ($activePreset == 'last_year'): ?>
Tidak ada transaksi penjualan yang tercatat untuk tahun lalu (<?php echo date('Y') - 1; ?>).
<?php else: ?>
Coba pilih rentang tanggal atau preset tanggal yang berbeda.
<?php endif; ?>
</p>
<a href="?reset=1" class="btn btn-primary">
<i class="fas fa-redo"></i> Reset Filter
</a>
</div>
<?php else: ?>
<!-- Grid View (Default) - FIXED VERSION -->
<div class="view-content" id="grid-view">
<div class="top-products-grid">
<?php
mysqli_data_seek($result, 0); // Reset pointer
$rank = 1;
while ($product = mysqli_fetch_assoc($result)):
?>
<div class="product-card">
<div class="card h-100">
<div class="rank-badge <?php echo ($rank <= 3) ? 'top-3' : ''; ?>">
<?php echo $rank++; ?>
</div>
<div class="top-product position-relative ">
<!-- Move badge outside card-body but still inside top-product -->
<div class="card-body">
<h5 class="card-title"><?php echo $product['nama_barang']; ?></h5>
<p class="card-text text-muted">Kode: <?php echo $product['kode_barang']; ?></p>
<p class="card-text"><span class="badge bg-info"><?php echo $product['kategori']; ?></span></p>
<div class="product-stats">
<div class="d-flex justify-content-between">
<span>Terjual:</span>
<span class="fw-bold"><?php echo number_format($product['total_quantity']); ?> unit</span>
</div>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<div class="d-flex justify-content-between">
<span>Harga:</span>
<span class="fw-bold">Rp. <?php echo number_format($product['harga']); ?></span>
</div>
<div class="d-flex justify-content-between">
<span>Total:</span>
<span class="fw-bold">Rp. <?php echo number_format($product['total_revenue']); ?></span>
</div>
<?php endif; ?>
<!-- Progress bar showing percentage of total sales -->
<div class="progress product-progress mt-2">
<div class="progress-bar bg-success" role="progressbar"
style="width: <?php echo min(100, ($product['total_quantity'] / $summary['total_items_sold']) * 100); ?>%"
aria-valuenow="<?php echo ($product['total_quantity'] / $summary['total_items_sold']) * 100; ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<small class="text-muted mt-1 d-block">
<?php echo number_format(($product['total_quantity'] / $summary['total_items_sold']) * 100, 1); ?>% dari total penjualan
</small>
</div>
</div>
</div>
</div>
</div>
<?php endwhile; ?>
</div>
</div>
<!-- Table View (Hidden by default) - FIXED VERSION -->
<div class="view-content" id="table-view" style="display: none;">
<div class="table-responsive">
<table class="table table-bordered table-striped datanew">
<thead>
<tr>
<th width="70">Rank</th>
<th>Kode</th>
<th>Nama Produk</th>
<th>Kategori</th>
<th>Jumlah Terjual</th>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<th>Harga Satuan</th>
<th>Total Pendapatan</th>
<?php endif; ?>
<th>% dari Total</th>
</tr>
</thead>
<tbody>
<?php
mysqli_data_seek($result, 0); // Reset pointer
$rank = 1;
while ($product = mysqli_fetch_assoc($result)):
$percentOfTotal = ($product['total_quantity'] / $summary['total_items_sold']) * 100;
?>
<tr>
<td class="text-center">
<?php if ($rank <= 3): ?>
<span class="badge bg-warning">#<?php echo $rank++; ?></span>
<?php else: ?>
<?php echo $rank++; ?>
<?php endif; ?>
</td>
<td><?php echo $product['kode_barang']; ?></td>
<td><?php echo $product['nama_barang']; ?></td>
<td><span class="badge bg-info"><?php echo $product['kategori']; ?></span></td>
<td><?php echo number_format($product['total_quantity']); ?></td>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<td>Rp. <?php echo number_format($product['harga']); ?></td>
<td>Rp. <?php echo number_format($product['total_revenue']); ?></td>
<?php endif; ?>
<td>
<div class="d-flex align-items-center">
<div class="progress flex-grow-1 me-2" style="height: 6px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: <?php echo min(100, $percentOfTotal); ?>%"
aria-valuenow="<?php echo $percentOfTotal; ?>"
aria-valuemin="0"
aria-valuemax="100">
</div>
</div>
<span><?php echo number_format($percentOfTotal, 1); ?>%</span>
</div>
</td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Summary Section - Admin Only -->
<?php if ($isAdmin || canAccessFeature('view_summary')): ?>
<div class="row mt-4 no-print">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">Rangkuman untuk Periode Terpilih</h5>
<h6><?php echo date('d M Y', strtotime($startDate)); ?> - <?php echo date('d M Y', strtotime($endDate)); ?></h6>
</div>
<div class="card-body">
<div class="report-summary">
<div class="row">
<div class="col-md-4">
<div class="report-summary-item">
<span>Total Produk Terjual:</span>
<strong><?php echo number_format($summary['total_products']); ?> produk</strong>
</div>
<div class="report-summary-item">
<span>Total Unit Terjual:</span>
<strong><?php echo number_format($summary['total_items_sold']); ?> unit</strong>
</div>
</div>
<div class="col-md-4">
<div class="report-summary-item">
<span>Rata-rata Per Hari:</span>
<strong><?php
$daysDiff = (strtotime($endDate) - strtotime($startDate)) / (60 * 60 * 24) + 1;
$dailyAvgUnits = $daysDiff > 0 ? $summary['total_items_sold'] / $daysDiff : 0;
echo number_format($dailyAvgUnits, 1);
?> unit/hari</strong>
</div>
<div class="report-summary-item">
<span>Pendapatan Rata-rata Per Hari:</span>
<strong>Rp. <?php
$dailyAvgRevenue = $daysDiff > 0 ? $summary['total_revenue'] / $daysDiff : 0;
echo number_format($dailyAvgRevenue);
?></strong>
</div>
</div>
<div class="col-md-4">
<div class="report-summary-item">
<span>Total Pendapatan:</span>
<strong>Rp. <?php echo number_format($summary['total_revenue']); ?></strong>
</div>
<div class="report-summary-item">
<span>Rata-rata Pendapatan Per Produk:</span>
<strong>Rp. <?php
$avgRevenuePerProduct = $summary['total_products'] > 0 ? $summary['total_revenue'] / $summary['total_products'] : 0;
echo number_format($avgRevenuePerProduct);
?></strong>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<!-- Employee-only section: Activity log -->
<?php if (!$isAdmin): ?>
<div class="row mt-4">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">Ringkasan Aktivitas Anda</h5>
</div>
<div class="card-body">
<div class="alert alert-info">
<i class="fa fa-info-circle me-2"></i>
Untuk laporan keuangan terperinci atau untuk mengekspor data, silakan hubungi administrator Anda.
</div>
<div class="report-summary">
<div class="row">
<div class="col-md-6">
<div class="report-summary-item">
<span>Produk Dilihat:</span>
<strong><?php echo $limit; ?> produk teratas</strong>
</div>
<div class="report-summary-item">
<span>Total Unit Terjual (Diproses):</span>
<strong><?php echo number_format($summary['total_items_sold']); ?> unit</strong>
</div>
</div>
<div class="col-md-6">
<div class="report-summary-item">
<span>Laporan Dibuat:</span>
<strong><?php echo date('d M Y H:i'); ?></strong>
</div>
<div class="report-summary-item">
<span>Level Akses:</span>
<strong>Karyawan</strong>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Employee Permission Modal -->
<div class="modal fade" id="permissionModal" tabindex="-1" aria-labelledby="permissionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning">
<h5 class="modal-title" id="permissionModalLabel">
<i class="fas fa-exclamation-triangle me-2"></i> Akses Dibatasi
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center mb-4">
<i class="fas fa-lock" style="font-size: 3rem; color: #ff9f43; margin-bottom: 15px;"></i>
<h4>Fitur Dibatasi</h4>
</div>
<p>Fitur ini hanya tersedia untuk administrator dan personel yang berwenang.</p>
<p>Akun karyawan tidak memiliki akses ke fungsi cetak atau ekspor.</p>
<div class="alert alert-info mt-3" id="actionDetails">
<!-- Will be filled dynamically -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
<!-- JavaScript libraries -->
<script src="../../../bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="../../../bootstrap/assets/js/feather.min.js"></script>
<script src="../../../bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="../../../bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="../../../bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="../../../bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="../../../bootstrap/assets/js/moment.min.js"></script>
<script src="../../../bootstrap/assets/js/bootstrap-datetimepicker.min.js"></script>
<script src="../../../bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="../../../bootstrap/assets/plugins/sweetalert/sweetalert2.all.min.js"></script>
<script src="../../../bootstrap/assets/plugins/sweetalert/sweetalerts.min.js"></script>
<script src="../../../bootstrap/assets/js/script.js"></script>
<!-- Chart.js for visualizations -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<!-- Initialize Charts for Category Data -->
<script>
// Function to initialize Chart.js charts
function initializeCharts() {
<?php if ($queryData['has_data'] && $categories && mysqli_num_rows($categories) > 0): ?>
// Prepare category data for charts
var categoryData = {
labels: [],
quantities: [],
revenues: [],
products: [],
colors: [
'#FF9F43', '#7367F0', '#00CFE8', '#28C76F', '#EA5455',
'#9F44D3', '#1E9FF2', '#F6416C', '#28a745', '#17a2b8',
'#fd7e14', '#6c757d', '#343a40', '#20c997', '#6610f2'
]
};
<?php
mysqli_data_seek($categories, 0); // Reset pointer
$categoryCount = 0;
while ($category = mysqli_fetch_assoc($categories)):
$categoryCount++;
?>
categoryData.labels.push('<?php echo $category['kategori']; ?>');
categoryData.quantities.push(<?php echo $category['total_quantity']; ?>);
categoryData.revenues.push(<?php echo $category['total_revenue']; ?>);
categoryData.products.push(<?php echo $category['unique_products']; ?>);
<?php endwhile; ?>
// Fill in any missing colors needed
while (categoryData.colors.length < categoryData.labels.length) {
const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
categoryData.colors.push(randomColor);
}
// Pie Chart for category distribution
var pieCtx = document.getElementById('categoryPieChart').getContext('2d');
var categoryPieChart = new Chart(pieCtx, {
type: 'pie',
data: {
labels: categoryData.labels,
datasets: [{
data: categoryData.quantities,
backgroundColor: categoryData.colors,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
position: 'right',
labels: {
boxWidth: 15,
padding: 15
}
},
tooltips: {
callbacks: {
label: function(tooltipItem, data) {
const category = data.labels[tooltipItem.index];
const quantity = data.datasets[0].data[tooltipItem.index];
const totalQuantity = data.datasets[0].data.reduce((a, b) => a + b, 0);
const percentage = ((quantity / totalQuantity) * 100).toFixed(1);
return `${category}: ${quantity} unit (${percentage}%)`;
}
}
},
title: {
display: true,
text: 'Distribusi Unit Terjual per Kategori'
}
}
});
// Bar Chart for quantity comparison
var barCtx = document.getElementById('categoryBarChart').getContext('2d');
var categoryBarChart = new Chart(barCtx, {
type: 'bar',
data: {
labels: categoryData.labels,
datasets: [{
label: 'Unit Terjual',
data: categoryData.quantities,
backgroundColor: categoryData.colors,
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
},
legend: {
display: false
}
}
});
<?php endif; ?>
}
</script>
<!-- Custom JavaScript for popular products report -->
<script src="popular-products.js"></script>
<script>
// Disable console logs and warnings
if (window.location.hostname === 'localhost') {
console.log = function() {}; // Disable console logs
console.warn = function() {}; // Disable console warnings
console.error = function() {}; // Disable console errors
window.alert = function() {}; // Disable alert popups
}
</script>
</body>
</html>

View File

@ -0,0 +1,421 @@
<?php
session_start();
// Include database connection
include('../../../routes/db_conn.php');
// Check if user is logged in, redirect to login page if not
if (!isset($_SESSION['user_id']) || !isset($_SESSION['role'])) {
header('Location: /ayula-store/views/login/');
exit;
}
// Get current logged-in user's information
function getCurrentUser() {
global $conn;
if (isset($_SESSION['user_id'])) {
$stmt = mysqli_prepare($conn, "SELECT id_kasir, username, role, phone FROM kasir WHERE id_kasir = ?");
mysqli_stmt_bind_param($stmt, "i", $_SESSION['user_id']);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
return $user;
}
return null;
}
// Ensure username is available in session
if (isset($_SESSION['user_id']) && !isset($_SESSION['username'])) {
$currentUser = getCurrentUser();
if ($currentUser) {
$_SESSION['username'] = $currentUser['username'];
}
}
// Get user role from session
function getUserRole() {
return isset($_SESSION['role']) ? $_SESSION['role'] : 'employee';
}
// Get username from session
function getUsername() {
return isset($_SESSION['username']) ? $_SESSION['username'] : 'Unknown User';
}
// Check if current user is admin
function isAdmin() {
return getUserRole() === 'admin';
}
// Define date presets with role-based options
function getDatePresets($isAdmin = false) {
$presets = [
'today' => [
'label' => 'Hari Ini',
'start' => date('Y-m-d'),
'end' => date('Y-m-d')
],
'yesterday' => [
'label' => 'Kemarin',
'start' => date('Y-m-d', strtotime('-1 day')),
'end' => date('Y-m-d', strtotime('-1 day'))
],
'this_week' => [
'label' => 'Minggu Ini',
'start' => date('Y-m-d', strtotime('monday this week')),
'end' => date('Y-m-d')
],
'last_week' => [
'label' => 'Minggu Lalu',
'start' => date('Y-m-d', strtotime('monday last week')),
'end' => date('Y-m-d', strtotime('sunday last week'))
],
'this_month' => [
'label' => 'Bulan Ini',
'start' => date('Y-m-01'),
'end' => date('Y-m-d')
],
];
// Admin-only presets
if ($isAdmin) {
$presets['last_month'] = [
'label' => 'Bulan Lalu',
'start' => date('Y-m-d', strtotime('first day of last month')),
'end' => date('Y-m-d', strtotime('last day of last month'))
];
$presets['last_90_days'] = [
'label' => '90 Hari Terakhir',
'start' => date('Y-m-d', strtotime('-90 days')),
'end' => date('Y-m-d')
];
$presets['this_year'] = [
'label' => 'Tahun Ini',
'start' => date('Y') . '-01-01',
'end' => date('Y-m-d')
];
$presets['all_time'] = [
'label' => 'Sepanjang Waktu',
'start' => '2000-01-01', // Set a reasonable start date
'end' => date('Y-m-d')
];
}
return $presets;
}
// Function to check if an employee has permission for a specific report feature
function canAccessFeature($feature) {
// Default permissions based on role
$permissions = [
'admin' => [
'export_excel' => true,
'export_pdf' => true,
'print_report' => true,
'view_financial' => true,
'view_payment_methods' => true,
'view_all_time' => true,
'view_summary' => true,
'view_popular_products' => true
],
'manager' => [
'export_excel' => true,
'export_pdf' => false,
'print_report' => true,
'view_financial' => true,
'view_payment_methods' => true,
'view_all_time' => false,
'view_summary' => true,
'view_popular_products' => true
],
'employee' => [
'export_excel' => false,
'export_pdf' => false,
'print_report' => false,
'view_financial' => false,
'view_payment_methods' => false,
'view_all_time' => false,
'view_summary' => false,
'view_popular_products' => false
],
'user' => [
'export_excel' => false,
'export_pdf' => false,
'print_report' => false,
'view_financial' => false,
'view_payment_methods' => false,
'view_all_time' => false,
'view_summary' => false,
'view_popular_products' => false
]
];
$role = getUserRole();
// If role doesn't exist in our permissions, default to employee
if (!isset($permissions[$role])) {
$role = 'employee';
}
// Return permission status
return isset($permissions[$role][$feature]) ? $permissions[$role][$feature] : false;
}
// Function to get popular products
function getPopularProducts($startDate, $endDate, $limit = 20, $sortBy = 'quantity', $isAdmin = false) {
global $conn;
// Add time components to make the date range inclusive
$startDateTime = $startDate . ' 00:00:00';
$endDateTime = $endDate . ' 23:59:59';
// Base query for popular products - modified to join jenis_barang for category
$baseQuery = "
SELECT
b.id_barang,
b.kode_barang,
b.nama_barang,
b.harga,
j.nama_jenis as kategori,
SUM(dt.jumlah) as total_quantity,
SUM(dt.total_harga) as total_revenue
FROM
barang b
JOIN
jenis_barang j ON b.id_jenis = j.id_jenis
JOIN
detail_transaksi dt ON b.id_barang = dt.id_barang
JOIN
transaksi t ON dt.id_transaksi = t.id_transaksi
WHERE
t.tanggal BETWEEN ? AND ?
GROUP BY
b.id_barang
ORDER BY
";
// Add the appropriate ORDER BY clause
if ($sortBy == 'revenue') {
$baseQuery .= "total_revenue DESC";
} else {
$baseQuery .= "total_quantity DESC";
}
// Add limit
$baseQuery .= " LIMIT ?";
// Prepare and execute query
$stmt = mysqli_prepare($conn, $baseQuery);
if (!$stmt) {
return [
'result' => false,
'error' => 'Failed to prepare query: ' . mysqli_error($conn),
'has_data' => false
];
}
mysqli_stmt_bind_param($stmt, "ssi", $startDateTime, $endDateTime, $limit);
$execResult = mysqli_stmt_execute($stmt);
if (!$execResult) {
return [
'result' => false,
'error' => 'Failed to execute query: ' . mysqli_stmt_error($stmt),
'has_data' => false
];
}
$result = mysqli_stmt_get_result($stmt);
$rowCount = mysqli_num_rows($result);
// Get summary totals
$summaryQuery = "
SELECT
COUNT(DISTINCT dt.id_barang) as total_products,
SUM(dt.jumlah) as total_items_sold,
SUM(dt.total_harga) as total_revenue
FROM
detail_transaksi dt
JOIN
transaksi t ON dt.id_transaksi = t.id_transaksi
WHERE
t.tanggal BETWEEN ? AND ?
";
$summaryStmt = mysqli_prepare($conn, $summaryQuery);
if (!$summaryStmt) {
return [
'result' => $result,
'summary' => null,
'error' => 'Failed to prepare summary query: ' . mysqli_error($conn),
'has_data' => ($rowCount > 0)
];
}
mysqli_stmt_bind_param($summaryStmt, "ss", $startDateTime, $endDateTime);
$summaryExecResult = mysqli_stmt_execute($summaryStmt);
if (!$summaryExecResult) {
return [
'result' => $result,
'summary' => null,
'error' => 'Failed to execute summary query: ' . mysqli_stmt_error($summaryStmt),
'has_data' => ($rowCount > 0)
];
}
$summaryResult = mysqli_stmt_get_result($summaryStmt);
$summary = mysqli_fetch_assoc($summaryResult);
// Handle case where no data is found
if (!$summary) {
$summary = [
'total_products' => 0,
'total_items_sold' => 0,
'total_revenue' => 0
];
}
// For non-admin users, potentially mask certain financial data
if (!$isAdmin) {
// Keep the summary accessible but hide financial details if needed
if (!canAccessFeature('view_financial')) {
$summary['total_revenue'] = 0;
}
}
// Get category breakdown for pie chart - using jenis_barang table
$categoryQuery = "
SELECT
j.nama_jenis as kategori,
SUM(dt.jumlah) as total_quantity,
SUM(dt.total_harga) as total_revenue,
COUNT(DISTINCT b.id_barang) as unique_products
FROM
barang b
JOIN
jenis_barang j ON b.id_jenis = j.id_jenis
JOIN
detail_transaksi dt ON b.id_barang = dt.id_barang
JOIN
transaksi t ON dt.id_transaksi = t.id_transaksi
WHERE
t.tanggal BETWEEN ? AND ?
GROUP BY
j.nama_jenis
ORDER BY
total_quantity DESC
";
$categoryStmt = mysqli_prepare($conn, $categoryQuery);
if (!$categoryStmt) {
return [
'result' => $result,
'summary' => $summary,
'categories' => null,
'error' => 'Failed to prepare category query: ' . mysqli_error($conn),
'has_data' => ($rowCount > 0)
];
}
mysqli_stmt_bind_param($categoryStmt, "ss", $startDateTime, $endDateTime);
$categoryExecResult = mysqli_stmt_execute($categoryStmt);
if (!$categoryExecResult) {
return [
'result' => $result,
'summary' => $summary,
'categories' => null,
'error' => 'Failed to execute category query: ' . mysqli_stmt_error($categoryStmt),
'has_data' => ($rowCount > 0)
];
}
$categoryResult = mysqli_stmt_get_result($categoryStmt);
return [
'result' => $result,
'summary' => $summary,
'categories' => $categoryResult,
'has_data' => ($rowCount > 0)
];
}
// Function to apply date ranges based on preset or manual selection
function getStartDate($datePresets, $activePreset) {
// If preset is selected, use preset dates
if (!empty($activePreset) && isset($datePresets[$activePreset])) {
return $datePresets[$activePreset]['start'];
}
// Otherwise use the date parameters if provided
$startDate = isset($_GET['start_date']) && !empty($_GET['start_date'])
? $_GET['start_date']
: date('Y-m-01'); // Default to first day of current month
// Make sure date is in YYYY-MM-DD format
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate)) {
$startDate = date('Y-m-01');
}
return $startDate;
}
function getEndDate($datePresets, $activePreset) {
// If preset is selected, use preset dates
if (!empty($activePreset) && isset($datePresets[$activePreset])) {
return $datePresets[$activePreset]['end'];
}
// Otherwise use the date parameters if provided
$endDate = isset($_GET['end_date']) && !empty($_GET['end_date'])
? $_GET['end_date']
: date('Y-m-d'); // Default to today
// Make sure date is in YYYY-MM-DD format
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
$endDate = date('Y-m-d');
}
return $endDate;
}
// Handle PDF export functionality
function exportToPDF($startDate, $endDate, $limit, $sortBy, $isAdmin) {
// Check permissions
if (!($isAdmin || canAccessFeature('export_pdf'))) {
die("Access denied. You don't have permission to export to PDF.");
}
// Get the data
$queryData = getPopularProducts($startDate, $endDate, $limit, $sortBy, $isAdmin);
// Here you would implement PDF generation with a library like FPDF or TCPDF
// For example:
/*
require('fpdf/fpdf.php');
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial', 'B', 16);
$pdf->Cell(40, 10, 'Ayula Store - Laporan Produk Terlaris');
// Add more PDF generation code here
$pdf->Output('D', 'produk_terlaris.pdf');
exit;
*/
// For now, just return a message
echo "<h1>PDF Export</h1>";
echo "<p>This functionality would generate a PDF for the selected date range: $startDate to $endDate</p>";
echo "<p>Implementation with a PDF library is required to complete this feature.</p>";
exit;
}

View File

@ -0,0 +1,305 @@
/* Custom styles for popular products report */
.product-card {
transition: all 0.3s ease;
border-radius: 10px;
overflow: hidden;
margin-bottom: 20px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
position: relative;
display: flex;
flex-direction: column;
height: 100%;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
/* Move rank badge outside card body */
.rank-badge {
position: absolute;
top: 10px; /* Slightly above card */
left: 15px;
width: 40px;
height: 40px;
background-color: #7367f0;
color: white;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
font-size: 18px;
z-index: 10;
}
/* Special style for top 3 rankings */
.top-3 {
background-color: #ff9f43;
font-size: 20px;
}
/* Ensure card title and content don't overlap */
.top-product .card-body {
padding-top: 60px; /* Push content below rank badge */
padding-left: 15px;
padding-right: 15px;
position: relative;
}
.top-product .card-title {
font-size: 16px;
margin: 0;
padding: 10px 0;
background-color: #f8f9fa;
border-bottom: 1px solid #e9ecef;
font-weight: 600;
}
/* Add spacing between card content */
.product-stats {
margin-top: 20px; /* Ensures enough space between content and stats */
}
/* Ensure the progress bar has some margin */
.product-progress {
height: 8px;
margin-top: 10px; /* Space out progress bar from content */
}
/* Top products grid styles */
.top-products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-top: 15px;
}
/* Responsive adjustment */
@media (max-width: 768px) {
.top-products-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.rank-badge {
width: 35px;
height: 35px;
font-size: 16px;
top: -10px;
}
.product-card .card-body {
padding-top: 70px;
}
}
/* Ensure cards are aligned properly on small screens */
@media (max-width: 576px) {
.product-card .card {
min-height: 250px;
}
}
/* Progress bar styles */
.product-progress {
height: 8px;
margin-top: 5px;
}
/* Top products grid styles */
.top-products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-top: 15px;
}
/* Memastikan card memiliki tinggi minimum */
.product-card .card {
min-height: 200px;
height: 100%;
width: 100%; /* Pastikan lebar penuh */
display: flex;
flex-direction: column;
margin-left: auto; /* Pindahkan kartu ke kanan dengan margin kiri otomatis */
margin-right: 0; /* Menjaga margin kanan tidak tumpang tindih */
}
/* Custom styles for view toggle buttons */
.view-toggle .btn {
padding: 0.25rem 0.5rem;
}
.view-toggle .btn i {
margin-right: 0;
}
/* Chart container styles */
.chart-container {
position: relative;
height: 300px;
width: 100%;
}
/* Employee-specific warning styles */
.employee-warning {
display: none;
}
.employee .employee-warning {
display: block;
padding: 5px 10px;
border-radius: 4px;
background-color: #fff8e1;
border-left: 4px solid #ffc107;
margin-bottom: 15px;
}
/* Role-based styling */
.admin-only {
display: none;
}
.admin .admin-only {
display: block;
}
.employee .sensitive-financial {
filter: blur(4px);
position: relative;
}
.employee .sensitive-financial:hover::after {
content: "Data terbatas";
position: absolute;
left: 0;
top: 0;
background: rgba(255,0,0,0.2);
padding: 2px 5px;
border-radius: 3px;
font-size: 10px;
white-space: nowrap;
}
/* Disabled buttons for employees */
.employee .disabled-for-employee {
opacity: 0.6;
pointer-events: none;
}
.employee .request-only {
display: inline-block;
}
.admin .request-only {
display: none;
}
/* Styling untuk tabel view */
#table-view th:first-child {
width: 70px; /* Lebar tetap untuk kolom ranking */
text-align: center;
}
#table-view .badge {
font-size: 14px;
padding: 5px 8px;
}
/* Print specific styles */
@media print {
.sidebar, .header, .no-print, #filter_inputs, .wordset, .search-set {
display: none !important;
}
.page-wrapper {
margin-left: 0 !important;
padding: 0 !important;
}
.card {
box-shadow: none !important;
margin-bottom: 1rem !important;
}
.content {
padding: 0 !important;
}
.table {
width: 100% !important;
}
.table th, .table td {
padding: 0.25rem !important;
}
.report-header {
display: block !important;
text-align: center;
margin-bottom: 20px;
}
.report-header h2 {
margin-bottom: 5px;
}
/* Force table view in print mode */
#table-view {
display: block !important;
}
#grid-view {
display: none !important;
}
/* Hide chart canvases as they don't print well */
canvas {
display: none !important;
}
}
/* Responsive adjustments */
@media (max-width: 768px) {
.top-products-grid {
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.dash-widget {
margin-bottom: 15px;
}
.chart-container {
height: 250px;
}
.rank-badge {
width: 30px;
height: 30px;
font-size: 14px;
}
.rank-badge.top-3 {
width: 35px;
height: 35px;
font-size: 16px;
}
.top-product .card-title {
font-size: 14px;
min-height: 40px;
}
}
/* Make the table more compact on smaller screens */
@media (max-width: 576px) {
.datanew th, .datanew td {
padding: 0.5rem 0.25rem;
font-size: 0.875rem;
}
.view-toggle {
margin-left: auto;
}
}

View File

@ -0,0 +1,460 @@
/**
* popular-products.js - Script for handling product popularity report functionality
*
* This script manages:
* - DataTable initialization
* - Date filtering and form handling
* - View toggling (grid vs table)
* - Export functionality (PDF, Excel)
* - Chart initialization
*/
// Document ready function
$(document).ready(function () {
// Initialize datatable with error handling
initializeDataTable();
// Handle date preset selection
handleDatePresets();
// Handle limit and sort changes
handleFilterChanges();
// Form validation
setupFormValidation();
// View toggle
setupViewToggle();
// Export functionality
setupExportFunctionality();
// Initialize Chart.js charts if they exist
if (typeof initializeCharts === 'function') {
initializeCharts();
}
// Hide loading overlay when page is fully loaded
hideLoadingWithDelay(500);
// Check user role for hiding UI elements
checkUserPermissions();
});
/**
* Check user permissions and adjust UI accordingly
*/
function checkUserPermissions() {
// Check if body has class 'employee' or 'user'
if ($('body').hasClass('employee') || $('body').hasClass('user')) {
// Hide any admin-only elements that might not have been hidden by PHP
$('.admin-only').hide();
}
}
/**
* Initialize the DataTable with appropriate settings
*/
function initializeDataTable() {
try {
$('.datanew').DataTable({
responsive: true,
language: {
search: '<span>Cari:</span> _INPUT_',
searchPlaceholder: 'Cari produk...',
lengthMenu: '<span>Tampilkan:</span> _MENU_',
paginate: {
'first': 'Pertama',
'last': 'Terakhir',
'next': 'Berikutnya',
'previous': 'Sebelumnya'
},
info: "Menampilkan _START_ sampai _END_ dari _TOTAL_ entri",
infoEmpty: "Menampilkan 0 sampai 0 dari 0 entri",
infoFiltered: "(disaring dari _MAX_ entri total)",
emptyTable: "Tidak ada data tersedia untuk periode yang dipilih",
zeroRecords: "Tidak ditemukan catatan yang cocok"
},
dom: '<"top"fl>rt<"bottom"ip><"clear">',
lengthMenu: [10, 25, 50, 100],
pageLength: 10,
drawCallback: function() {
hideLoadingOverlay();
}
});
} catch (e) {
console.error("DataTable initialization error:", e);
hideLoadingOverlay();
}
}
/**
* Handle date preset selection changes
*/
function handleDatePresets() {
$('#preset').on('change', function() {
if ($(this).val() === '') {
// Custom date range selected - show custom date inputs
$('#custom-date-inputs').show();
} else {
// Preset selected - hide custom date inputs and show loading overlay
$('#custom-date-inputs').hide();
showLoadingOverlay();
// Add a small delay before submitting to ensure loading overlay is visible
setTimeout(function() {
$('#date-filter-form').submit();
}, 100);
}
});
}
/**
* Handle limit and sort_by changes (auto-submit form)
*/
function handleFilterChanges() {
$('#limit, #sort_by').on('change', function() {
showLoadingOverlay();
// Add a small delay before submitting
setTimeout(function() {
$('#date-filter-form').submit();
}, 100);
});
// Handle reset button - show loading overlay
$('a[href="?reset=1"]').on('click', function() {
showLoadingOverlay();
});
}
/**
* Setup form validation
*/
function setupFormValidation() {
$('#date-filter-form').on('submit', function(e) {
showLoadingOverlay();
// Only validate if custom date range is selected
if ($('#preset').val() === '') {
var startDate = $('#start_date').val();
var endDate = $('#end_date').val();
if (!startDate || !endDate) {
alert('Harap pilih tanggal awal dan akhir');
hideLoadingOverlay();
e.preventDefault();
return false;
}
if (new Date(startDate) > new Date(endDate)) {
alert('Tanggal awal tidak boleh lebih besar dari tanggal akhir');
hideLoadingOverlay();
e.preventDefault();
return false;
}
}
return true;
});
}
/**
* Setup view toggle between grid and table
*/
function setupViewToggle() {
$('.view-toggle button').on('click', function() {
const viewType = $(this).data('view');
// Update active button
$('.view-toggle button').removeClass('active');
$(this).addClass('active');
// Show/hide appropriate view
if (viewType === 'grid') {
$('#grid-view').show();
$('#table-view').hide();
} else {
$('#grid-view').hide();
$('#table-view').show();
}
});
}
/**
* Setup export functionality
*/
function setupExportFunctionality() {
// PDF Export
$('.pdf-export').on('click', function(e) {
e.preventDefault();
showLoadingOverlay();
// Check if the user has permission (controlled by CSS class)
if ($(this).hasClass('disabled-for-employee')) {
showPermissionModal('Ekspor PDF', 'Laporan ini tidak dapat diekspor ke PDF oleh akun karyawan.');
hideLoadingOverlay();
return;
}
// If we have permission, proceed with export
exportToPDF();
});
// Excel Export
$('.excel-export').on('click', function(e) {
e.preventDefault();
showLoadingOverlay();
// Check if the user has permission
if ($(this).hasClass('disabled-for-employee')) {
showPermissionModal('Ekspor Excel', 'Laporan ini tidak dapat diekspor ke Excel oleh akun karyawan.');
hideLoadingOverlay();
return;
}
// If we have permission, proceed with export
exportToExcel();
});
// Print Report
$('.print-report').on('click', function(e) {
e.preventDefault();
// Check if the user has permission
if ($(this).hasClass('disabled-for-employee')) {
showPermissionModal('Cetak Laporan', 'Pencetakan laporan dibatasi hanya untuk akun administrator.');
return;
}
// If we have permission, proceed with print
printReport();
});
// For employee access - handle restricted action attempts
$('.employee-print, .employee-export').on('click', function(e) {
e.preventDefault();
// Get the action type from data attribute or default
var actionType = $(this).data('action') === 'print_attempt' ? 'Cetak Laporan' : 'Ekspor Laporan';
var message = $(this).data('action') === 'print_attempt'
? 'Pencetakan laporan dibatasi hanya untuk akun administrator.'
: 'Ekspor data dibatasi hanya untuk akun administrator.';
showPermissionModal(actionType, message);
});
}
/**
* Show permission denied modal
*/
function showPermissionModal(actionType, message) {
// Set the action details
$('#actionDetails').html(
'<strong>Tindakan yang Dicoba:</strong> ' + actionType + '<br>' +
'<small>' + message + '</small>'
);
// Show the modal
var permissionModal = new bootstrap.Modal(document.getElementById('permissionModal'));
permissionModal.show();
}
/**
* Print the current report
*/
function printReport() {
// Add print-specific styling
$('<style>')
.attr('type', 'text/css')
.html('@media print { ' +
'.no-print, .dataTables_filter, .dataTables_length, .dataTables_paginate, .sidebar, .header, #sidebar, #mobile_btn { display: none !important; } ' +
'.page-wrapper { margin-left: 0 !important; padding: 20px !important; } ' +
'.card { border: none !important; box-shadow: none !important; } ' +
'thead { background-color: #f8f9fa !important; -webkit-print-color-adjust: exact !important; color-adjust: exact !important; } ' +
'table { width: 100% !important; } ' +
'.table th, .table td { padding: 0.25rem !important; } ' +
'.report-header { text-align: center; margin-bottom: 20px; } ' +
'.report-header h3 { margin-bottom: 5px; } ' +
'#table-view { display: block !important; } ' +
'#grid-view { display: none !important; } ' +
'}')
.appendTo('head');
// Add a report header for printing
if ($('.report-header').length === 0) {
// Get date range for report header
var startDate = $('#start_date').val() || $('.alert-info strong').next().text().split(' sampai ')[0];
var endDate = $('#end_date').val() || $('.alert-info strong').next().text().split(' sampai ')[1];
$('.content').prepend(
'<div class="report-header no-screen" style="display:none;">' +
'<h2>Ayula Store - Laporan Produk Terlaris</h2>' +
'<p>Periode: ' + startDate + ' sampai ' + endDate + '</p>' +
'<p>Dibuat pada: ' + new Date().toLocaleDateString() + '</p>' +
'</div>'
);
}
// Force table view for printing
$('#grid-view').hide();
$('#table-view').show();
window.print();
// Restore the original view after printing
if ($('.view-toggle button[data-view="grid"]').hasClass('active')) {
$('#grid-view').show();
$('#table-view').hide();
}
}
/**
* Export table to Excel (CSV)
*/
function exportToExcel() {
// Switch to table view temporarily
$('#grid-view').hide();
$('#table-view').show();
// Get the table data
var table = $('.datanew').DataTable();
var data = [];
try {
// Get all rows from the table (not just the current page)
table.rows().every(function() {
data.push(this.data());
});
var headers = [];
// Get headers
$('.datanew thead th').each(function () {
headers.push($(this).text());
});
// Create CSV content
var csvContent = "data:text/csv;charset=utf-8," + headers.join(",") + "\n";
// Add data rows
for (var i = 0; i < data.length; i++) {
var row = [];
for (var j = 0; j < data[i].length; j++) {
// Clean the data (remove HTML tags)
var cellData = data[i][j].toString().replace(/<[^>]*>/g, '').trim();
// Replace multiple spaces with a single space
cellData = cellData.replace(/\s+/g, ' ');
// Wrap in quotes and escape internal quotes
row.push('"' + cellData.replace(/"/g, '""') + '"');
}
csvContent += row.join(",") + "\n";
}
// Create download link
var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
// Use the current date in the filename
var today = new Date();
var dateStr = today.getFullYear() + '-' +
('0' + (today.getMonth()+1)).slice(-2) + '-' +
('0' + today.getDate()).slice(-2);
link.setAttribute("download", "produk_terlaris_" + dateStr + ".csv");
document.body.appendChild(link);
// Download the file
link.click();
// Clean up
document.body.removeChild(link);
} catch (e) {
console.error("Error exporting to Excel:", e);
alert("Terjadi kesalahan saat mengekspor. Silakan coba lagi.");
}
// Hide loading overlay when export is complete
hideLoadingOverlay();
// Restore the original view after exporting
if ($('.view-toggle button[data-view="grid"]').hasClass('active')) {
$('#grid-view').show();
$('#table-view').hide();
}
}
/**
* Export to PDF via server
*/
function exportToPDF() {
try {
// Create a form for server-side generation
var form = $('<form action="" method="post" target="_blank"></form>');
form.append('<input type="hidden" name="export_pdf" value="1">');
form.append('<input type="hidden" name="start_date" value="' + $('#start_date').val() + '">');
form.append('<input type="hidden" name="end_date" value="' + $('#end_date').val() + '">');
form.append('<input type="hidden" name="limit" value="' + $('#limit').val() + '">');
form.append('<input type="hidden" name="sort_by" value="' + $('#sort_by').val() + '">');
// Add the form to the document and submit it
$('body').append(form);
form.submit();
form.remove();
} catch (e) {
console.error("Error exporting to PDF:", e);
alert("Terjadi kesalahan saat mengekspor ke PDF. Silakan coba lagi.");
}
// Hide loading overlay after 2 seconds (since PDF generation happens in new tab)
setTimeout(function() {
hideLoadingOverlay();
}, 2000);
}
/**
* Show loading overlay
*/
function showLoadingOverlay() {
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'flex';
}
}
/**
* Hide loading overlay
*/
function hideLoadingOverlay() {
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
}
/**
* Hide loading overlay with delay
*/
function hideLoadingWithDelay(delay) {
setTimeout(function() {
hideLoadingOverlay();
}, delay || 500);
}
// Hide loading overlay when page is loaded
window.addEventListener('load', function() {
hideLoadingWithDelay(500);
});
// Failsafe timeout to hide loading overlay after 10 seconds
setTimeout(function() {
hideLoadingOverlay();
}, 10000);
// Disable console logs in production
if (window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1') {
console.log = function() {}; // Disable console logs
console.warn = function() {}; // Disable console warnings
console.error = function() {}; // Disable console errors
}

View File

@ -0,0 +1,745 @@
<?php
// Include configuration and database queries
require_once 'salesreport-config.php';
$username = getUsername();
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="description" content="POS - Bootstrap Admin Template">
<meta name="keywords" content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects">
<meta name="author" content="Dreamguys - Bootstrap Admin Template">
<meta name="robots" content="noindex, nofollow">
<title>Ayula Store POS - Laporan Penjualan</title>
<link rel="shortcut icon" type="image/x-icon" href="../../../src/img/smallest-ayula.png">
<link rel="stylesheet" href="../../../bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/css/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="../../../bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="../../../bootstrap/assets/css/style.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="salesreport.css">
<!-- Tambahkan kode CSS ini di bagian head -->
<style>
/* Loading overlay styles */
#loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.8);
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.loading-spinner {
border: 5px solid #f3f3f3;
border-top: 5px solid #7367f0;
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
.loading-text {
font-size: 18px;
font-weight: bold;
color: #333;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* No data message styles */
.no-data-container {
text-align: center;
padding: 40px 20px;
background-color: #f8f9fa;
border-radius: 8px;
margin: 20px 0;
}
.no-data-icon {
font-size: 3rem;
margin-bottom: 20px;
color: #6c757d;
}
.no-data-message {
font-size: 1.25rem;
font-weight: 500;
margin-bottom: 15px;
color: #343a40;
}
.no-data-help {
color: #6c757d;
margin-bottom: 20px;
}
</style>
</head>
<body class="<?php echo $isAdmin ? 'admin' : 'employee'; ?>">
<div id="global-loader">
<div class="whirly-loader"> </div>
</div>
<!-- Loading overlay -->
<div id="loading-overlay">
<div class="loading-spinner"></div>
<div class="loading-text">Memuat data...</div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img"><img src="../../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img"><img src="../../../src/img/userprofile.png" alt="">
<span class="status online"></span></span>
<div class="profilesets">
<h6><?php echo $isAdmin ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($username); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../../views/logout.php"><img
src="../../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown"
aria-expanded="false"><i class="fa fa-ellipsis-v"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../../views/logout.php"><img
src="../../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/dashboard/"><img src="../../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li>
<a href="/ayula-store/views/transaction/"><img src="../../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
POS</span></a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Produk</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php">Daftar Produk</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Produk</a></li>
<li><a href="categorylist.html">Daftar Kategori</a></li>
<li><a href="addcategory.html">Tambah Kategori</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../../bootstrap/assets/img/icons/purchase1.svg" alt="img" /><span>
Pembelian</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="purchaselist.html">Daftar Pembelian</a></li>
<li><a href="addpurchase.html">Tambah Pembelian</a></li>
<li><a href="importpurchase.html">Import Pembelian</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../../bootstrap/assets/img/icons/time.svg" alt="img" /><span>
Laporan</span>
<span class="menu-arrow"></span></a>
<ul>
<li>
<a href="purchaseorderreport.html">Laporan Order Pembelian</a>
</li>
<li><a href="inventoryreport.html">Laporan Inventaris</a></li>
<li><a href="/ayula-store/views/report/sales-report/" class="active">Laporan Penjualan</a></li>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/report/popular-products/">Produk Terlaris</a></li>
<?php } ?>
<li><a href="invoicereport.html">Laporan Faktur</a></li>
<li><a href="purchasereport.html">Laporan Pembelian</a></li>
<li><a href="supplierreport.html">Laporan Pemasok</a></li>
<li><a href="customerreport.html">Laporan Pelanggan</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Laporan Penjualan</h4>
<h6>Kelola Laporan Penjualan Anda</h6>
<?php if (!$isAdmin): ?>
<div class="alert alert-info mt-2 employee-warning">
<small><i class="fa fa-info-circle me-1"></i> Anda melihat laporan ini dengan akses karyawan. Beberapa data mungkin dibatasi.</small>
</div>
<?php endif; ?>
</div>
</div>
<!-- Dashboard stat widgets -->
<div class="row">
<div class="col-lg-4 col-sm-6 col-12 d-flex">
<div class="dash-widget flex-fill">
<div class="dash-widgetimg">
<span class="dash-widget-icon "><i class="fa fa-shopping-cart"></i></span>
</div>
<div class="dash-widgetcontent">
<h5>Total <span class="counters"><?php echo number_format($totals['total_transactions']); ?></span></h5>
<h6>Transaksi</h6>
</div>
</div>
</div>
<div class="col-lg-4 col-sm-6 col-12 d-flex">
<div class="dash-widget flex-fill">
<div class="dash-widgetimg">
<span class="dash-widget-icon"><i class="fa fa-cubes"></i></span>
</div>
<div class="dash-widgetcontent">
<h5>Total <span class="counters"><?php echo number_format($totals['total_items']); ?></span></h5>
<h6>Item Terjual</h6>
</div>
</div>
</div>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<div class="col-lg-4 col-sm-6 col-12 d-flex">
<div class="dash-widget flex-fill">
<div class="dash-widgetimg">
<span class="dash-widget-icon"><i class="fa fa-money-bill-alt"></i></span>
</div>
<div class="dash-widgetcontent">
<h5>Rp. <span class="counters"><?php echo number_format($totals['grand_total']); ?></span></h5>
<h6>Total Penjualan</h6>
</div>
</div>
</div>
<?php else: ?>
<!-- Employee placeholders for layout consistency -->
<div class="col-lg-6 col-sm-12 col-12 d-flex">
<div class="dash-widget flex-fill">
<div class="dash-widgetimg">
<span class="dash-widget-icon"><i class="fa fa-chart-line"></i></span>
</div>
<div class="dash-widgetcontent">
<h5>Laporan Aktivitas</h5>
<h6>Hubungi admin untuk detail keuangan</h6>
</div>
</div>
</div>
<?php endif; ?>
</div>
<!-- Enhanced Date Filter with Presets -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Filter Tanggal</h5>
</div>
<div class="card-body">
<form action="" method="GET" id="date-filter-form">
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="preset">Rentang Tanggal:</label>
<select name="preset" id="preset" class="form-control">
<?php if ($isAdmin): ?>
<option value="">Kustom</option>
<?php endif; ?>
<?php
// Buat array terjemahan untuk label preset
$presetLabels = [
'today' => 'Hari Ini',
'yesterday' => 'Kemarin',
'this_week' => 'Minggu Ini',
'last_week' => 'Minggu Lalu',
'this_month' => 'Bulan Ini',
'last_month' => 'Bulan Lalu',
// 'last_90_days' => '90 Hari Terakhir',
'all_time' => 'Sepanjang Waktu'
];
foreach ($datePresets as $key => $preset):
$label = isset($presetLabels[$key]) ? $presetLabels[$key] : $preset['label'];
?>
<option value="<?php echo $key; ?>" <?php echo ($activePreset === $key) ? 'selected' : ''; ?>>
<?php echo $label; ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<?php if ($isAdmin): ?>
<!-- Custom Date Inputs - Admin Only -->
<div class="col-md-8">
<div class="row custom-date-inputs" id="custom-date-inputs" <?php echo !empty($activePreset) ? 'style="display:none;"' : ''; ?>>
<div class="col-md-5">
<div class="form-group">
<label for="start_date">Dari Tanggal:</label>
<input type="date" id="start_date" name="start_date" class="form-control"
value="<?php echo $startDate; ?>">
</div>
</div>
<div class="col-md-5">
<div class="form-group">
<label for="end_date">Sampai Tanggal:</label>
<input type="date" id="end_date" name="end_date" class="form-control"
value="<?php echo $endDate; ?>">
</div>
</div>
<div class="col-md-2">
<div class="form-group">
<label>&nbsp;</label>
<button type="submit" class="btn btn-primary w-100" id="search-button">
<i class="fas fa-search"></i> Cari
</button>
</div>
</div>
</div>
</div>
<?php else: ?>
<!-- For employees - simpler view -->
<div class="col-md-8">
<div class="form-group">
<label>&nbsp;</label>
<button type="submit" class="btn btn-primary" id="search-button">
<i class="fas fa-search"></i> Terapkan Filter
</button>
</div>
</div>
<?php endif; ?>
</div>
<div class="row mt-3">
<div class="col-12 d-flex justify-content-end">
<a href="?reset=1" class="btn btn-secondary me-2">
<i class="fas fa-redo"></i> Reset
</a>
<?php if ($isAdmin || canAccessFeature('print_report')): ?>
<a href="#" class="btn btn-info me-2 print-report">
<i class="fas fa-print"></i> Cetak
</a>
<?php else: ?>
<a href="#" class="btn btn-info me-2 employee-print request-only"
data-action="print_attempt">
<i class="fas fa-print"></i> Cetak
</a>
<?php endif; ?>
<?php if ($isAdmin || canAccessFeature('export_excel') || canAccessFeature('export_pdf')): ?>
<div class="btn-group">
<button type="button" class="btn btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-download"></i> Ekspor
</button>
<ul class="dropdown-menu">
<?php if ($isAdmin || canAccessFeature('export_excel')): ?>
<li><a class="dropdown-item excel-export" href="#"><i class="fas fa-file-excel me-2"></i> Excel</a></li>
<?php endif; ?>
<?php if ($isAdmin || canAccessFeature('export_pdf')): ?>
<li><a class="dropdown-item pdf-export" href="#"><i class="fas fa-file-pdf me-2"></i> PDF</a></li>
<?php endif; ?>
</ul>
</div>
<?php else: ?>
<!-- Employee-limited export (with logging) -->
<a href="#" class="btn btn-success request-only employee-export"
data-action="export_attempt">
<i class="fas fa-download"></i> Ekspor
</a>
<?php endif; ?>
</div>
</div>
</form>
</div>
</div>
<!-- Current Date Range Display -->
<div class="alert alert-info mb-4">
<strong>Rentang Tanggal Saat Ini:</strong> <?php echo date('d M Y', strtotime($startDate)); ?> sampai
<?php echo date('d M Y', strtotime($endDate)); ?>
<?php if (!empty($activePreset)):
$presetLabels = [
'today' => 'Hari Ini',
'yesterday' => 'Kemarin',
'this_week' => 'Minggu Ini',
'last_week' => 'Minggu Lalu',
'this_month' => 'Bulan Ini',
'last_30_days' => '30 Hari Terakhir',
'last_month' => 'Bulan Lalu',
'last_90_days' => '90 Hari Terakhir',
'last_quarter' => 'Kuartal Terakhir',
'last_year' => 'Tahun Lalu',
'all_time' => 'Sepanjang Waktu'
];
$label = isset($presetLabels[$activePreset]) ? $presetLabels[$activePreset] : $datePresets[$activePreset]['label'];
?>
<span class="badge bg-primary ms-2"><?php echo $label; ?></span>
<?php endif; ?>
<div class="small mt-1">
<?php
$daysDiff = (strtotime($endDate) - strtotime($startDate)) / (60 * 60 * 24) + 1;
echo "Menampilkan data untuk " . number_format($daysDiff) . " hari";
?>
</div>
</div>
<!-- Sales Report Table -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">Transaksi Penjualan</h5>
</div>
<div class="card-body">
<?php if (isset($queryData['has_data']) && $queryData['has_data'] === false): ?>
<!-- Tampilan No Data Found (Tidak ada data) -->
<div class="no-data-container">
<div class="no-data-icon">
<i class="fas fa-search"></i>
</div>
<h4 class="no-data-message">Tidak ada transaksi ditemukan untuk periode yang dipilih</h4>
<p class="no-data-help">
<?php if ($activePreset == 'last_year'): ?>
Tidak ada transaksi penjualan yang tercatat untuk tahun lalu (<?php echo date('Y') - 1; ?>).
<?php else: ?>
Coba pilih rentang tanggal atau preset tanggal yang berbeda.
<?php endif; ?>
</p>
<a href="?reset=1" class="btn btn-primary">
<i class="fas fa-redo"></i> Reset Filter
</a>
</div>
<?php else: ?>
<!-- Tampilan Table Normal (Ada data) -->
<div class="table-responsive">
<table class="table datanew">
<thead>
<tr>
<th>ID Transaksi</th>
<th>Tanggal</th>
<th>Item</th>
<th>Produk</th>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<th>Total</th>
<?php endif; ?>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<th>Tunai</th>
<th>Kembalian</th>
<?php else: ?>
<th>Status</th>
<?php endif; ?>
<th class="text-end no-print">Aksi</th>
</tr>
</thead>
<tbody>
<?php if ($result && mysqli_num_rows($result) > 0): ?>
<?php mysqli_data_seek($result, 0); // Reset pointer to beginning of result set
?>
<?php while ($row = mysqli_fetch_assoc($result)): ?>
<tr>
<td><?php echo $row['kode_transaksi']; ?></td>
<td><?php echo date('d M Y H:i', strtotime($row['tanggal'])); ?></td>
<td><?php echo $row['total_item']; ?></td>
<td><?php echo $row['total_products']; ?></td>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<!-- Admin sees all financial details -->
<td>Rp. <?php echo number_format($row['total']); ?></td>
<?php endif; ?>
<?php if ($isAdmin || canAccessFeature('view_financial')): ?>
<td>Rp. <?php echo number_format($row['cash_amount']); ?></td>
<td>Rp. <?php echo number_format($row['change_amount']); ?></td>
<?php else: ?>
<!-- Employee sees limited details -->
<td><span class="badge bg-success">Selesai</span></td>
<?php endif; ?>
<td class="text-end no-print">
<a class="btn btn-sm btn-secondary" href="../../transaction/transaction_success.php?id=<?php echo $row['id_transaksi']; ?>">
<i class="fas fa-eye"></i> Lihat
</a>
<?php if ($isAdmin || canAccessFeature('print_report')): ?>
<a class="btn btn-sm btn-primary" href="#" onclick="printReceipt(<?php echo $row['id_transaksi']; ?>); return false;">
<i class="fas fa-print"></i> Cetak
</a>
<?php endif; ?>
</td>
</tr>
<?php endwhile; ?>
<?php else: ?>
<tr>
<td colspan="9" class="text-center">Tidak ada transaksi ditemukan untuk periode yang dipilih</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
<!-- Period Summary Report - Admin Only -->
<?php if ($isAdmin || canAccessFeature('view_summary')): ?>
<div class="row mt-4 no-print">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">Ringkasan untuk Periode yang Dipilih</h5>
<h6><?php echo date('d M Y', strtotime($startDate)); ?> - <?php echo date('d M Y', strtotime($endDate)); ?></h6>
</div>
<div class="card-body">
<div class="report-summary">
<div class="row">
<div class="col-md-6">
<div class="report-summary-item">
<span>Total Transaksi:</span>
<strong><?php echo number_format($totals['total_transactions']); ?></strong>
</div>
<div class="report-summary-item">
<span>Total Item Terjual:</span>
<strong><?php echo number_format($totals['total_items']); ?></strong>
</div>
</div>
<div class="col-md-6">
<div class="report-summary-item">
<span>Total:</span>
<strong>Rp. <?php echo number_format($totals['grand_total']); ?></strong>
</div>
<div class="report-summary-item">
<span>Rata-rata Harian:</span>
<strong>Rp. <?php
$daysDiff = (strtotime($endDate) - strtotime($startDate)) / (60 * 60 * 24) + 1;
$dailyAvg = $daysDiff > 0 ? $totals['grand_total'] / $daysDiff : 0;
echo number_format($dailyAvg);
?></strong>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<!-- Employee-only section: Activity log -->
<?php if (!$isAdmin): ?>
<div class="row mt-4">
<div class="col-lg-12">
<div class="card">
<div class="card-header">
<h5 class="card-title">Ringkasan Aktivitas Anda</h5>
</div>
<div class="card-body">
<div class="alert alert-info">
<i class="fa fa-info-circle me-2"></i>
Untuk laporan keuangan yang lebih rinci atau untuk mengekspor data, silakan hubungi administrator Anda.
</div>
<div class="report-summary">
<div class="row">
<div class="col-md-6">
<div class="report-summary-item">
<span>Transaksi Dilihat:</span>
<strong><?php echo $totals['total_transactions']; ?></strong>
</div>
<div class="report-summary-item">
<span>Item Diproses:</span>
<strong><?php echo $totals['total_items']; ?></strong>
</div>
</div>
<div class="col-md-6">
<div class="report-summary-item">
<span>Laporan Dibuat:</span>
<strong><?php echo date('d M Y H:i'); ?></strong>
</div>
<div class="report-summary-item">
<span>Level Akses:</span>
<strong>Karyawan</strong>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Employee Permission Modal -->
<div class="modal fade" id="permissionModal" tabindex="-1" aria-labelledby="permissionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning">
<h5 class="modal-title" id="permissionModalLabel">
<i class="fas fa-exclamation-triangle me-2"></i> Akses Dibatasi
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center mb-4">
<i class="fas fa-lock" style="font-size: 3rem; color: #ff9f43; margin-bottom: 15px;"></i>
<h4>Fitur Dibatasi</h4>
</div>
<p>Fitur ini hanya tersedia untuk administrator dan personel yang berwenang.</p>
<p>Akun karyawan tidak memiliki akses ke fungsi cetak atau ekspor.</p>
<div class="alert alert-info mt-3" id="actionDetails">
<!-- Akan diisi secara dinamis -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
<!-- JavaScript libraries -->
<script src="../../../bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="../../../bootstrap/assets/js/feather.min.js"></script>
<script src="../../../bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="../../../bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="../../../bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="../../../bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="../../../bootstrap/assets/js/moment.min.js"></script>
<script src="../../../bootstrap/assets/js/bootstrap-datetimepicker.min.js"></script>
<script src="../../../bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="../../../bootstrap/assets/plugins/sweetalert/sweetalert2.all.min.js"></script>
<script src="../../../bootstrap/assets/plugins/sweetalert/sweetalerts.min.js"></script>
<script src="../../../bootstrap/assets/js/script.js"></script>
<!-- Custom JavaScript -->
<script src="salesreport.js"></script>
<script>
// Fungsi untuk menyembunyikan loading overlay saat halaman dimuat
window.addEventListener('load', function() {
setTimeout(function() {
document.getElementById('loading-overlay').style.display = 'none';
}, 500); // 500ms delay untuk memastikan semuanya dimuat
});
// Jika halaman terlalu lama dimuat, sembunyikan loading overlay setelah 10 detik
setTimeout(function() {
document.getElementById('loading-overlay').style.display = 'none';
}, 10000);
// Tambahan untuk penanganan filter dengan preset
document.addEventListener('DOMContentLoaded', function() {
const presetSelect = document.getElementById('preset');
const customDateInputs = document.getElementById('custom-date-inputs');
if (presetSelect && customDateInputs) {
presetSelect.addEventListener('change', function() {
if (this.value === '') {
customDateInputs.style.display = 'flex';
} else {
// Tampilkan loading overlay saat memilih preset
document.getElementById('loading-overlay').style.display = 'flex';
// Tambahkan timeout kecil untuk memastikan loading screen muncul
setTimeout(function() {
document.getElementById('date-filter-form').submit();
}, 100);
}
});
}
});
</script>
<script>
// Nonaktifkan console logs dan warnings
if (window.location.hostname === 'localhost') {
console.log = function() {}; // Nonaktifkan console logs
console.warn = function() {}; // Nonaktifkan console warnings
console.error = function() {}; // Nonaktifkan console errors
window.alert = function() {}; // Nonaktifkan alert popups
}
</script>
</body>
</html>

View File

@ -0,0 +1,515 @@
<?php
// Mulai sesi untuk menangani keranjang
session_start();
// Include database connection
include('../../../routes/db_conn.php');
// Check if user is logged in, redirect to login page if not
if (!isset($_SESSION['user_id']) || !isset($_SESSION['role'])) {
header('Location: /ayula-store/views/login/');
exit;
}
// Get current logged-in user's information
function getCurrentUser() {
global $conn;
if (isset($_SESSION['user_id'])) {
$stmt = mysqli_prepare($conn, "SELECT id_kasir, username, role, phone FROM kasir WHERE id_kasir = ?");
mysqli_stmt_bind_param($stmt, "i", $_SESSION['user_id']);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
return $user;
}
return null;
}
// Ensure username is available in session
if (isset($_SESSION['user_id']) && !isset($_SESSION['username'])) {
$currentUser = getCurrentUser();
if ($currentUser) {
$_SESSION['username'] = $currentUser['username'];
}
}
// Get user role from session
function getUserRole() {
return isset($_SESSION['role']) ? $_SESSION['role'] : 'employee';
}
// Get username from session
function getUsername() {
return isset($_SESSION['username']) ? $_SESSION['username'] : 'Unknown User';
}
// Check if current user is admin
function isAdmin() {
return getUserRole() === 'admin';
}
// Definisikan preset tanggal dengan opsi berbasis peran
function getDatePresets($isAdmin = false) {
$presets = [
'today' => [
'label' => 'Hari Ini',
'start' => date('Y-m-d'),
'end' => date('Y-m-d')
],
'yesterday' => [
'label' => 'Kemarin',
'start' => date('Y-m-d', strtotime('-1 day')),
'end' => date('Y-m-d', strtotime('-1 day'))
],
'this_week' => [
'label' => 'Minggu Ini',
'start' => date('Y-m-d', strtotime('monday this week')),
'end' => date('Y-m-d')
],
'last_week' => [
'label' => 'Minggu Lalu',
'start' => date('Y-m-d', strtotime('monday last week')),
'end' => date('Y-m-d', strtotime('sunday last week'))
],
'this_month' => [
'label' => 'Bulan Ini',
'start' => date('Y-m-01'),
'end' => date('Y-m-d')
],
];
// Preset hanya untuk admin
if ($isAdmin) {
$presets['last_month'] = [
'label' => 'Bulan Lalu',
'start' => date('Y-m-d', strtotime('first day of last month')),
'end' => date('Y-m-d', strtotime('last day of last month'))
];
$presets['last_90_days'] = [
'label' => '90 Hari Terakhir',
'start' => date('Y-m-d', strtotime('-90 days')),
'end' => date('Y-m-d')
];
$presets['all_time'] = [
'label' => 'Sepanjang Waktu',
'start' => '2000-01-01', // Tetapkan tanggal mulai yang masuk akal
'end' => date('Y-m-d')
];
}
return $presets;
}
// Dapatkan preset tanggal yang sesuai berdasarkan peran pengguna
$isAdmin = isAdmin();
$datePresets = getDatePresets($isAdmin);
// Tangani permintaan reset
if (isset($_GET['reset']) && $_GET['reset'] == 1) {
// Jika reset diminta, alihkan ke halaman yang sama dengan tanggal default
header("Location: index.php");
exit;
}
// Jika pengguna bukan admin, batasi akses data historis
if (!$isAdmin && empty($_GET['preset']) && empty($_GET['start_date'])) {
// Default pengguna non-admin ke minggu ini jika tidak ada tanggal yang ditentukan
$_GET['preset'] = 'this_week';
}
// Periksa apakah preset dipilih
$activePreset = isset($_GET['preset']) && !empty($_GET['preset']) ? $_GET['preset'] : '';
// Terapkan rentang tanggal berdasarkan preset atau pemilihan manual
function getStartDate() {
global $datePresets, $activePreset;
// Jika preset dipilih, gunakan tanggal preset
if (!empty($activePreset) && isset($datePresets[$activePreset])) {
return $datePresets[$activePreset]['start'];
}
// Jika tidak, gunakan parameter tanggal jika disediakan
$startDate = isset($_GET['start_date']) && !empty($_GET['start_date'])
? $_GET['start_date']
: date('Y-m-01'); // Default ke hari pertama bulan ini
// Pastikan tanggal dalam format YYYY-MM-DD
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate)) {
$startDate = date('Y-m-01');
}
return $startDate;
}
function getEndDate() {
global $datePresets, $activePreset;
// Jika preset dipilih, gunakan tanggal preset
if (!empty($activePreset) && isset($datePresets[$activePreset])) {
return $datePresets[$activePreset]['end'];
}
// Jika tidak, gunakan parameter tanggal jika disediakan
$endDate = isset($_GET['end_date']) && !empty($_GET['end_date'])
? $_GET['end_date']
: date('Y-m-d'); // Default ke hari ini
// Pastikan tanggal dalam format YYYY-MM-DD
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate)) {
$endDate = date('Y-m-d');
}
return $endDate;
}
$startDate = getStartDate();
$endDate = getEndDate();
// Pastikan tanggal mulai tidak setelah tanggal akhir
if (strtotime($startDate) > strtotime($endDate)) {
$temp = $startDate;
$startDate = $endDate;
$endDate = $temp;
}
// Fungsi untuk mendapatkan kueri yang sesuai dengan peran dengan penanganan tidak ada data yang lebih baik
function getReportQueries($startDate, $endDate, $isAdmin = false) {
global $conn;
// Tambahkan komponen waktu untuk membuat rentang tanggal inklusif
$startDateTime = $startDate . ' 00:00:00';
$endDateTime = $endDate . ' 23:59:59';
// Siapkan kueri transaksi dasar dengan filter tanggal
$baseQuery = "SELECT t.id_transaksi, t.kode_transaksi, t.tanggal, t.total_item,
t.subtotal, t.total, t.metode_pembayaran, t.cash_amount, t.change_amount,
COUNT(DISTINCT dt.id_barang) as total_products
FROM transaksi t
LEFT JOIN detail_transaksi dt ON t.id_transaksi = dt.id_transaksi
WHERE t.tanggal BETWEEN ? AND ?";
// Admin melihat semuanya
if ($isAdmin) {
$transactionQuery = $baseQuery . " GROUP BY t.id_transaksi ORDER BY t.tanggal DESC";
}
// Karyawan melihat data terbatas dan hanya transaksi terbaru
else {
// Untuk karyawan, kami membatasi jumlah catatan
$transactionQuery = $baseQuery . " GROUP BY t.id_transaksi ORDER BY t.tanggal DESC LIMIT 100";
}
// Siapkan dan jalankan kueri transaksi
$stmt = mysqli_prepare($conn, $transactionQuery);
if (!$stmt) {
// Tangani kesalahan persiapan kueri
return [
'result' => false,
'paymentResult' => null,
'totals' => [
'total_transactions' => 0,
'total_items' => 0,
'total_subtotal' => 0,
'grand_total' => 0
],
'error' => 'Gagal menyiapkan kueri: ' . mysqli_error($conn),
'has_data' => false
];
}
mysqli_stmt_bind_param($stmt, "ss", $startDateTime, $endDateTime);
$execResult = mysqli_stmt_execute($stmt);
if (!$execResult) {
// Tangani kesalahan eksekusi kueri
return [
'result' => false,
'paymentResult' => null,
'totals' => [
'total_transactions' => 0,
'total_items' => 0,
'total_subtotal' => 0,
'grand_total' => 0
],
'error' => 'Gagal mengeksekusi kueri: ' . mysqli_stmt_error($stmt),
'has_data' => false
];
}
$result = mysqli_stmt_get_result($stmt);
$rowCount = mysqli_num_rows($result);
// Dapatkan total untuk ringkasan
$totalQuery = "SELECT
COUNT(id_transaksi) as total_transactions,
SUM(total_item) as total_items,
SUM(subtotal) as total_subtotal,
SUM(total) as grand_total
FROM transaksi
WHERE tanggal BETWEEN ? AND ?";
$totalStmt = mysqli_prepare($conn, $totalQuery);
if (!$totalStmt) {
// Tangani kesalahan persiapan kueri total
return [
'result' => $result,
'paymentResult' => null,
'totals' => [
'total_transactions' => 0,
'total_items' => 0,
'total_subtotal' => 0,
'grand_total' => 0
],
'error' => 'Gagal menyiapkan kueri total: ' . mysqli_error($conn),
'has_data' => ($rowCount > 0)
];
}
mysqli_stmt_bind_param($totalStmt, "ss", $startDateTime, $endDateTime);
$totalExecResult = mysqli_stmt_execute($totalStmt);
if (!$totalExecResult) {
// Tangani kesalahan eksekusi kueri total
return [
'result' => $result,
'paymentResult' => null,
'totals' => [
'total_transactions' => 0,
'total_items' => 0,
'total_subtotal' => 0,
'grand_total' => 0
],
'error' => 'Gagal mengeksekusi kueri total: ' . mysqli_stmt_error($totalStmt),
'has_data' => ($rowCount > 0)
];
}
$totalResult = mysqli_stmt_get_result($totalStmt);
$totals = mysqli_fetch_assoc($totalResult);
// Tangani kasus di mana tidak ada data yang ditemukan
if (!$totals) {
$totals = [
'total_transactions' => 0,
'total_items' => 0,
'total_subtotal' => 0,
'grand_total' => 0
];
}
// Untuk pengguna non-admin, masker data keuangan tertentu
if (!$isAdmin && $totals) {
// Hanya tampilkan kuantitas, sembunyikan angka keuangan
if (!canAccessFeature('view_financial')) {
$totals['total_subtotal'] = 0;
$totals['grand_total'] = 0;
}
}
// Dapatkan breakdown metode pembayaran - hanya untuk pengguna dengan izin yang tepat
$paymentResult = null;
if (($isAdmin || canAccessFeature('view_payment_methods')) && $rowCount > 0) {
$paymentQuery = "SELECT
metode_pembayaran,
COUNT(*) as count,
SUM(total) as total_amount
FROM transaksi
WHERE tanggal BETWEEN ? AND ?
GROUP BY metode_pembayaran
ORDER BY total_amount DESC";
$paymentStmt = mysqli_prepare($conn, $paymentQuery);
if ($paymentStmt) {
mysqli_stmt_bind_param($paymentStmt, "ss", $startDateTime, $endDateTime);
$paymentExecResult = mysqli_stmt_execute($paymentStmt);
if ($paymentExecResult) {
$paymentResult = mysqli_stmt_get_result($paymentStmt);
}
}
}
return [
'result' => $result,
'paymentResult' => $paymentResult,
'totals' => $totals,
'has_data' => ($rowCount > 0)
];
}
// Fungsi untuk memeriksa apakah karyawan memiliki izin untuk fitur laporan tertentu
function canAccessFeature($feature) {
// Izin default berdasarkan peran
$permissions = [
'admin' => [
'export_excel' => true,
'export_pdf' => true,
'print_report' => true,
'view_financial' => true,
'view_payment_methods' => true,
'view_all_time' => true,
'view_summary' => true
],
'manager' => [
'export_excel' => true,
'export_pdf' => false,
'print_report' => true,
'view_financial' => true,
'view_payment_methods' => true,
'view_all_time' => false,
'view_summary' => true
],
'employee' => [
'export_excel' => false,
'export_pdf' => false,
'print_report' => false,
'view_financial' => false,
'view_payment_methods' => false,
'view_all_time' => false,
'view_summary' => false
]
];
$role = getUserRole();
// Jika peran tidak ada dalam izin kami, default ke karyawan
if (!isset($permissions[$role])) {
$role = 'employee';
}
// Kembalikan status izin
return isset($permissions[$role][$feature]) ? $permissions[$role][$feature] : false;
}
// Dapatkan kueri yang sesuai berdasarkan peran pengguna
$queryData = getReportQueries($startDate, $endDate, $isAdmin);
$result = $queryData['result'];
$paymentResult = $queryData['paymentResult'];
$totals = $queryData['totals'];
// Dapatkan peran pengguna dari sesi untuk izin menu
$userRole = getUserRole();
// Menangani permintaan ekspor PDF
if (isset($_POST['export_pdf']) || (isset($_GET['export']) && $_GET['export'] == 'pdf')) {
// Periksa izin
if (!($isAdmin || canAccessFeature('export_pdf'))) {
die("Akses ditolak. Anda tidak memiliki izin untuk mengekspor ke PDF.");
}
// Dapatkan parameter tanggal
$exportStartDate = isset($_POST['start_date']) ? $_POST['start_date'] : (isset($_GET['start_date']) ? $_GET['start_date'] : $startDate);
$exportEndDate = isset($_POST['end_date']) ? $_POST['end_date'] : (isset($_GET['end_date']) ? $_GET['end_date'] : $endDate);
// Tetapkan header untuk PDF (memaksa unduhan)
header('Content-Type: text/html; charset=utf-8');
// Mulai output buffering
ob_start();
// Konten HTML ramah PDF
echo '<!DOCTYPE html>';
echo '<html>';
echo '<head>';
echo '<meta charset="UTF-8">';
echo '<title>Laporan Penjualan</title>';
echo '<style>';
echo 'body { font-family: Arial, sans-serif; margin: 20px; }';
echo 'table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }';
echo 'th, td { border: 1px solid #000; padding: 8px; text-align: left; }';
echo 'th { background-color: #f2f2f2; font-weight: bold; }';
echo '.header { text-align: center; margin-bottom: 30px; }';
echo '.summary { margin: 20px 0; }';
echo '.footer { text-align: center; margin-top: 30px; font-size: 12px; color: #666; }';
echo '@media print { .no-print { display: none; } }';
echo '</style>';
echo '</head>';
echo '<body>';
// Tambahkan tombol cetak
echo '<div class="no-print" style="text-align: right; margin-bottom: 20px;">';
echo '<button onclick="window.print()">Cetak laporan ini</button>';
echo '<p>Setelah mencetak, gunakan dialog cetak browser Anda untuk menyimpan sebagai PDF</p>';
echo '</div>';
// Header laporan
echo '<div class="header">';
echo '<h1>Ayula Store - Laporan Penjualan</h1>';
echo '<h3>Periode: ' . date('d M Y', strtotime($exportStartDate)) . ' sampai ' . date('d M Y', strtotime($exportEndDate)) . '</h3>';
echo '<p>Dibuat pada: ' . date('d M Y H:i:s') . '</p>';
echo '<p>Dibuat oleh: ' . ($userRole) . '</p>';
echo '</div>';
// Jalankan kembali kueri untuk mendapatkan data baru
$exportQueryData = getReportQueries($exportStartDate, $exportEndDate, true);
$exportResult = $exportQueryData['result'];
$exportTotals = $exportQueryData['totals'];
// Bagian ringkasan
echo '<div class="summary">';
echo '<h2>Ringkasan</h2>';
echo '<table>';
echo '<tr><th style="width: 200px;">Total Transaksi</th><td>' . number_format($exportTotals['total_transactions']) . '</td></tr>';
echo '<tr><th>Total Item Terjual</th><td>' . number_format($exportTotals['total_items']) . '</td></tr>';
echo '<tr><th>Subtotal</th><td>Rp. ' . number_format($exportTotals['total_subtotal']) . '</td></tr>';
echo '<tr><th>Total</th><td>Rp. ' . number_format($exportTotals['grand_total']) . '</td></tr>';
$daysDiff = (strtotime($exportEndDate) - strtotime($exportStartDate)) / (60 * 60 * 24) + 1;
$dailyAvg = $daysDiff > 0 ? $exportTotals['grand_total'] / $daysDiff : 0;
echo '<tr><th>Rata-rata Harian</th><td>Rp. ' . number_format($dailyAvg) . '</td></tr>';
echo '</table>';
echo '</div>';
// Tabel transaksi
echo '<h2>Transaksi</h2>';
echo '<table>';
echo '<thead>';
echo '<tr>';
echo '<th>ID Transaksi</th>';
echo '<th>Tanggal</th>';
echo '<th>Item</th>';
echo '<th>Subtotal</th>';
echo '<th>Total</th>';
echo '<th>Pembayaran</th>';
echo '</tr>';
echo '</thead>';
echo '<tbody>';
if ($exportResult && mysqli_num_rows($exportResult) > 0) {
mysqli_data_seek($exportResult, 0); // Reset pointer ke awal set hasil
while ($row = mysqli_fetch_assoc($exportResult)) {
echo '<tr>';
echo '<td>' . $row['kode_transaksi'] . '</td>';
echo '<td>' . date('d M Y H:i', strtotime($row['tanggal'])) . '</td>';
echo '<td>' . $row['total_item'] . '</td>';
echo '<td>Rp. ' . number_format($row['subtotal']) . '</td>';
echo '<td>Rp. ' . number_format($row['total']) . '</td>';
echo '<td>' . $row['metode_pembayaran'] . '</td>';
echo '</tr>';
}
} else {
echo '<tr><td colspan="7" align="center">Tidak ada transaksi ditemukan</td></tr>';
}
echo '</tbody>';
echo '</table>';
// Footer
echo '<div class="footer">';
echo '<p>Ayula Store &copy; ' . date('Y') . ' - Hak Cipta Dilindungi</p>';
echo '<p>Laporan ini bersifat rahasia dan ditujukan hanya untuk personel yang berwenang.</p>';
echo '</div>';
echo '</body>';
echo '</html>';
// Keluarkan buffer dan akhiri
ob_end_flush();
exit;
}
?>

View File

@ -0,0 +1,270 @@
/* Custom styles for toolbar */
.multi-select-toolbar {
position: fixed;
top: 0; /* Position at the top */
left: 0;
width: 100%;
background-color: #ff9f43; /* Brand color */
color: #ffffff; /* White text */
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 15px;
display: none;
z-index: 9999; /* Highest z-index to ensure it's above everything else */
justify-content: space-between;
align-items: center;
}
.multi-select-toolbar.active {
display: flex;
}
/* Adjust all other page content when toolbar is active */
body.toolbar-active .header {
margin-top: 60px; /* Height of toolbar + padding */
}
body.toolbar-active .sidebar {
padding-top: 60px; /* Push sidebar content down */
}
body.toolbar-active .page-wrapper {
padding-top: 60px; /* Push main content down when toolbar is visible */
}
/* Make sure text is fully visible in toolbar */
.multi-select-toolbar .selected-count {
font-weight: bold;
padding: 5px;
margin: 0;
}
.multi-select-toolbar .toolbar-actions {
display: flex;
gap: 10px;
}
.multi-select-toolbar .toolbar-actions button {
white-space: nowrap;
padding: 8px 15px;
font-size: 14px;
}
/* Ensure the date picker stays on top */
.datetimepicker {
z-index: 999999 !important; /* Ensure it's on top of other elements */
}
/* Custom styles for dashboard stats */
.dash-widget-icon {
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-size: 24px;
background-color: rgba(255, 159, 67, 0.2);
color: #ff9f43;
}
.dash-count {
font-size: 20px;
font-weight: 700;
margin-bottom: 10px;
}
.dash-widget {
transition: all 0.3s ease;
}
.dash-widget:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
/* Report specific styles */
.report-card {
border-radius: 10px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.report-card .card-header {
background-color: #f9f9f9;
border-bottom: 1px solid #eee;
}
.report-summary {
background-color: #f8fbff;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.report-summary-item {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px dotted #ddd;
}
.report-summary-item:last-child {
border-bottom: none;
}
/* Role-based styling */
.admin-only {
display: none;
}
.admin .admin-only {
display: block;
}
.employee .sensitive-financial {
filter: blur(4px);
position: relative;
}
.employee .sensitive-financial:hover::after {
content: "Restricted data";
position: absolute;
left: 0;
top: 0;
background: rgba(255,0,0,0.2);
padding: 2px 5px;
border-radius: 3px;
font-size: 10px;
white-space: nowrap;
}
/* Employee specific warning */
.employee-warning {
display: none;
}
.employee .employee-warning {
display: block;
padding: 5px 10px;
border-radius: 4px;
background-color: #fff8e1;
border-left: 4px solid #ffc107;
margin-bottom: 15px;
}
/* Disabled buttons for employees */
.employee .disabled-for-employee {
opacity: 0.6;
pointer-events: none;
}
.employee .request-only {
display: inline-block;
}
.admin .request-only {
display: none;
}
/* Modal styling for employee permissions */
#permissionModal .modal-header {
border-bottom: 3px solid #dc3545;
background-color: #fff8f8;
color: #dc3545;
}
#permissionModal .modal-body {
padding: 25px;
}
#permissionModal .modal-footer {
border-top: 1px solid #eee;
background-color: #f9f9f9;
}
#permissionModal .fas.fa-lock {
color: #dc3545;
animation: pulse 1.5s ease-in-out infinite;
}
#permissionModal .btn-secondary {
background-color: #6c757d;
border-color: #6c757d;
}
#permissionModal .btn-secondary:hover {
background-color: #5a6268;
border-color: #545b62;
}
#permissionModal #actionDetails {
background-color: #f8f9fa;
border-left: 4px solid #dc3545;
padding: 10px 15px;
font-size: 0.9rem;
}
/* Animations */
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.1);
opacity: 0.8;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes fadeInScale {
from {
transform: scale(0.5);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* Print specific styles */
@media print {
.sidebar, .header, .no-print, #filter_inputs, .wordset, .search-set {
display: none !important;
}
.page-wrapper {
margin-left: 0 !important;
padding: 0 !important;
}
.card {
box-shadow: none !important;
margin-bottom: 1rem !important;
}
.content {
padding: 0 !important;
}
.table {
width: 100% !important;
}
.table th, .table td {
padding: 0.25rem !important;
}
.report-header {
display: block !important;
text-align: center;
margin-bottom: 20px;
}
.report-header h2 {
margin-bottom: 5px;
}
}

View File

@ -0,0 +1,358 @@
// salesreport.js - Versi yang ditingkatkan dengan penanganan tidak ada data
$(document).ready(function () {
// Inisialisasi datatable dengan penanganan kesalahan
try {
$('.datanew').DataTable({
responsive: true,
language: {
search: '<span>Cari:</span> _INPUT_',
searchPlaceholder: 'Cari transaksi...',
lengthMenu: '<span>Tampilkan:</span> _MENU_',
paginate: {
'first': 'Pertama',
'last': 'Terakhir',
'next': '>',
'previous': '<'
},
info: "Menampilkan _START_ sampai _END_ dari _TOTAL_ entri",
infoEmpty: "Menampilkan 0 sampai 0 dari 0 entri",
infoFiltered: "(disaring dari _MAX_ entri total)",
emptyTable: "Tidak ada data tersedia untuk periode yang dipilih",
zeroRecords: "Tidak ditemukan catatan yang cocok",
processing: "Memproses..."
},
dom: '<"top"fl>rt<"bottom"ip><"clear">',
lengthMenu: [10, 25, 50, 100],
pageLength: 10,
// Penanganan kesalahan untuk DataTables
drawCallback: function() {
// Sembunyikan loading overlay saat data siap
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
}
});
} catch (e) {
console.error("Kesalahan inisialisasi DataTable:", e);
// Sembunyikan loading overlay jika terjadi kesalahan
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
}
// Menangani pemilihan preset tanggal
$('#preset').on('change', function() {
if ($(this).val() === '') {
// Rentang tanggal kustom dipilih - tampilkan input tanggal kustom
$('#custom-date-inputs').show();
} else {
// Preset dipilih - sembunyikan input tanggal kustom dan tampilkan loading overlay
$('#custom-date-inputs').hide();
// Tampilkan loading overlay sebelum mengirimkan
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'flex';
}
// Tambahkan penundaan kecil sebelum mengirimkan untuk memastikan loading overlay terlihat
setTimeout(function() {
$('#date-filter-form').submit();
}, 100);
}
});
// Validasi form dasar
$('#date-filter-form').on('submit', function(e) {
// Tampilkan loading overlay
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'flex';
}
// Hanya validasi jika rentang tanggal kustom dipilih
if ($('#preset').val() === '') {
var startDate = $('#start_date').val();
var endDate = $('#end_date').val();
if (!startDate || !endDate) {
alert('Silakan pilih tanggal mulai dan tanggal akhir');
// Sembunyikan loading overlay jika validasi gagal
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
e.preventDefault();
return false;
}
if (new Date(startDate) > new Date(endDate)) {
alert('Tanggal mulai tidak boleh lebih besar dari tanggal akhir');
// Sembunyikan loading overlay jika validasi gagal
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
e.preventDefault();
return false;
}
}
return true;
});
// Fungsi ekspor
$('.pdf-export').on('click', function(e) {
e.preventDefault();
// Tampilkan loading overlay
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'flex';
}
// Periksa apakah pengguna memiliki izin (dikontrol oleh kelas CSS)
if ($(this).hasClass('disabled-for-employee')) {
showPermissionModal('Ekspor PDF', 'Laporan ini tidak dapat diekspor ke PDF oleh akun karyawan.');
// Sembunyikan loading overlay jika pemeriksaan izin gagal
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
return;
}
// Jika kita memiliki izin, lanjutkan dengan ekspor
exportTableToPDF();
});
$('.excel-export').on('click', function(e) {
e.preventDefault();
// Tampilkan loading overlay
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'flex';
}
// Periksa apakah pengguna memiliki izin (dikontrol oleh kelas CSS)
if ($(this).hasClass('disabled-for-employee')) {
showPermissionModal('Ekspor Excel', 'Laporan ini tidak dapat diekspor ke Excel oleh akun karyawan.');
// Sembunyikan loading overlay jika pemeriksaan izin gagal
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
return;
}
// Jika kita memiliki izin, lanjutkan dengan ekspor
exportTableToExcel();
});
$('.print-report').on('click', function(e) {
e.preventDefault();
// Periksa apakah pengguna memiliki izin (dikontrol oleh kelas CSS)
if ($(this).hasClass('disabled-for-employee')) {
showPermissionModal('Cetak Laporan', 'Pencetakan laporan dibatasi hanya untuk akun administrator.');
return;
}
// Jika kita memiliki izin, lanjutkan dengan cetak
printReport();
});
// Untuk akses karyawan - menangani upaya tindakan terbatas
$('.employee-print, .employee-export').on('click', function(e) {
e.preventDefault();
// Dapatkan jenis tindakan dari atribut data atau default
var actionType = $(this).data('action') === 'print_attempt' ? 'Cetak Laporan' : 'Ekspor Laporan';
var message = $(this).data('action') === 'print_attempt'
? 'Pencetakan laporan dibatasi hanya untuk akun administrator.'
: 'Ekspor data dibatasi hanya untuk akun administrator.';
showPermissionModal(actionType, message);
});
// Menangani tombol reset - tampilkan loading overlay
$('a[href="?reset=1"]').on('click', function() {
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'flex';
}
});
});
// Fungsi untuk menampilkan modal izin
function showPermissionModal(actionType, message) {
// Atur detail tindakan
$('#actionDetails').html(
'<strong>Tindakan yang Dicoba:</strong> ' + actionType + '<br>' +
'<small>' + message + '</small>'
);
// Tampilkan modal
var permissionModal = new bootstrap.Modal(document.getElementById('permissionModal'));
permissionModal.show();
}
// Fungsi untuk mencetak tanda terima
function printReceipt(transactionId) {
// Buka tanda terima di jendela baru untuk pencetakan
var printWindow = window.open('../../transaction/transaction_success.php?id=' + transactionId + '&print=true', '_blank', 'width=400,height=600');
// Cetak otomatis setelah konten dimuat
printWindow.onload = function () {
setTimeout(function () {
printWindow.print();
}, 500);
};
return false;
}
// Fungsi untuk mencetak laporan saat ini
function printReport() {
// Tambahkan gaya khusus untuk pencetakan
$('<style>')
.attr('type', 'text/css')
.html('@media print { ' +
'.no-print, .dataTables_filter, .dataTables_length, .dataTables_paginate, .sidebar, .header, #sidebar, #mobile_btn { display: none !important; } ' +
'.page-wrapper { margin-left: 0 !important; padding: 20px !important; } ' +
'.card { border: none !important; box-shadow: none !important; } ' +
'thead { background-color: #f8f9fa !important; -webkit-print-color-adjust: exact !important; color-adjust: exact !important; } ' +
'table { width: 100% !important; } ' +
'.table th, .table td { padding: 0.25rem !important; } ' +
'.report-header { text-align: center; margin-bottom: 20px; } ' +
'.report-header h3 { margin-bottom: 5px; } ' +
'}')
.appendTo('head');
// Tambahkan header laporan untuk pencetakan
if ($('.report-header').length === 0) {
// Dapatkan rentang tanggal untuk header laporan
var startDate = $('#start_date').val() || $('.alert-info strong').next().text().split(' sampai ')[0];
var endDate = $('#end_date').val() || $('.alert-info strong').next().text().split(' sampai ')[1];
$('.content').prepend(
'<div class="report-header no-screen" style="display:none;">' +
'<h2>Ayula Store - Laporan Penjualan</h2>' +
'<p>Periode: ' + startDate + ' sampai ' + endDate + '</p>' +
'<p>Dibuat pada: ' + new Date().toLocaleDateString() + '</p>' +
'</div>'
);
}
window.print();
}
// Fungsi untuk mengekspor tabel ke Excel
function exportTableToExcel() {
// Dapatkan data tabel
var table = $('.datanew').DataTable();
var data = [];
try {
// Dapatkan semua baris dari tabel (tidak hanya halaman saat ini)
table.rows().every(function() {
data.push(this.data());
});
var headers = [];
// Dapatkan header
$('.datanew thead th').each(function () {
// Lewati kolom Aksi
if ($(this).text() !== 'Aksi') {
headers.push($(this).text());
}
});
// Buat konten CSV
var csvContent = "data:text/csv;charset=utf-8," + headers.join(",") + "\n";
// Tambahkan baris data
for (var i = 0; i < data.length; i++) {
var row = [];
for (var j = 0; j < data[i].length - 1; j++) { // Lewati kolom terakhir (Aksi)
// Bersihkan data (hapus tag HTML)
var cellData = data[i][j].toString().replace(/<[^>]*>/g, '').trim();
// Ganti beberapa spasi dengan satu spasi
cellData = cellData.replace(/\s+/g, ' ');
// Bungkus dalam tanda kutip dan hindari tanda kutip internal
row.push('"' + cellData.replace(/"/g, '""') + '"');
}
csvContent += row.join(",") + "\n";
}
// Buat tautan unduhan
var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
// Gunakan tanggal saat ini dalam nama file
var today = new Date();
var dateStr = today.getFullYear() + '-' +
('0' + (today.getMonth()+1)).slice(-2) + '-' +
('0' + today.getDate()).slice(-2);
link.setAttribute("download", "laporan_penjualan_" + dateStr + ".csv");
document.body.appendChild(link);
// Unduh file
link.click();
// Bersihkan
document.body.removeChild(link);
} catch (e) {
console.error("Error mengekspor ke Excel:", e);
alert("Terjadi kesalahan saat mengekspor. Silakan coba lagi.");
}
// Sembunyikan loading overlay saat ekspor selesai
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
}
// Fungsi untuk mengekspor tabel ke PDF
function exportTableToPDF() {
try {
// Buat form untuk pembuatan di sisi server
var form = $('<form action="" method="post" target="_blank"></form>');
form.append('<input type="hidden" name="export_pdf" value="1">');
form.append('<input type="hidden" name="start_date" value="' + $('#start_date').val() + '">');
form.append('<input type="hidden" name="end_date" value="' + $('#end_date').val() + '">');
// Tambahkan form ke dokumen dan kirimkan
$('body').append(form);
form.submit();
form.remove();
} catch (e) {
console.error("Error mengekspor ke PDF:", e);
alert("Terjadi kesalahan saat mengekspor ke PDF. Silakan coba lagi.");
}
// Sembunyikan loading overlay setelah 2 detik (karena pembuatan PDF terjadi di tab baru)
setTimeout(function() {
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
}, 2000);
}
// Otomatis sembunyikan loading overlay saat halaman dimuat sepenuhnya
$(window).on('load', function() {
if (document.getElementById('loading-overlay')) {
setTimeout(function() {
document.getElementById('loading-overlay').style.display = 'none';
}, 500);
}
});
// Failsafe timeout untuk menyembunyikan loading overlay setelah 10 detik
setTimeout(function() {
if (document.getElementById('loading-overlay')) {
document.getElementById('loading-overlay').style.display = 'none';
}
}, 10000);
// Nonaktifkan console logs dalam produksi
if (window.location.hostname !== 'localhost') {
console.log = function() {}; // Nonaktifkan console logs
console.warn = function() {}; // Nonaktifkan console warnings
console.error = function() {}; // Nonaktifkan console errors
}

265
views/reporttt/report.css Normal file
View File

@ -0,0 +1,265 @@
/**
* Styling for Report Module
* Ayula Store
*/
.card-stats {
transition: all 0.3s ease;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
overflow: hidden;
}
.card-stats:hover {
transform: translateY(-5px);
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
}
.stat-icon {
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-size: 24px;
margin-right: 15px;
}
.stat-widget-three {
padding: 15px;
}
.stat-widget-three .order-summary-content h2 {
font-size: 28px;
font-weight: 600;
margin-bottom: 5px;
color: #333;
}
.stat-widget-three .order-summary-content span {
font-size: 14px;
color: #6c757d;
}
.report-filter-card {
background: #f9f9f9;
border-radius: 10px;
transition: all 0.3s ease;
margin-bottom: 25px;
}
.report-filter-card:hover {
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}
.report-filter-card .card-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
color: #333;
}
.report-filter-card .form-group label {
font-weight: 500;
margin-bottom: 8px;
color: #555;
}
.report-filter-card .form-control {
border-radius: 6px;
box-shadow: none;
border: 1px solid #ddd;
}
.report-filter-card .input-group-text {
background-color: #f0f3f5;
border: 1px solid #ddd;
}
.table th {
background-color: #5682d6 !important;
color: white !important;
font-weight: 600;
text-transform: uppercase;
font-size: 12px;
letter-spacing: 0.5px;
vertical-align: middle;
padding: 12px 15px;
border: none !important;
}
.table td {
vertical-align: middle;
padding: 12px 15px;
border-color: #f0f0f0;
font-size: 14px;
}
.table tbody tr:hover {
background-color: #f9fafc;
}
.receipt-thumbnail {
width: 60px;
height: 60px;
object-fit: cover;
border-radius: 5px;
cursor: pointer;
transition: transform 0.3s ease;
border: 2px solid #eee;
}
.receipt-thumbnail:hover {
transform: scale(1.1);
border-color: #5682d6;
}
#receipt-modal .modal-img {
width: 100%;
max-height: 80vh;
object-fit: contain;
border-radius: 5px;
border: 1px solid #eee;
}
.chart-container {
position: relative;
height: 300px;
margin-bottom: 30px;
}
.filter-btn {
margin-top: 32px;
height: 44px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.print-export-btn {
margin-right: 10px;
display: flex;
align-items: center;
}
.print-export-btn i {
margin-right: 5px;
}
.daterangepicker {
border-radius: 8px;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
border: 1px solid #ddd;
}
.daterangepicker .ranges li.active {
background-color: #5682d6;
}
.daterangepicker td.active,
.daterangepicker td.active:hover {
background-color: #5682d6;
}
.productimgname {
display: flex;
align-items: center;
}
.productimgname .product-img {
width: 40px;
height: 40px;
overflow: hidden;
margin-right: 10px;
border-radius: 5px;
border: 1px solid #eee;
}
.productimgname .product-img img {
width: 100%;
height: 100%;
object-fit: contain;
}
.productimgname a {
font-weight: 500;
color: #333;
text-decoration: none;
}
.productimgname a:hover {
color: #5682d6;
}
.table-responsive {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
scrollbar-width: thin;
scrollbar-color: #ddd #f9f9f9;
}
.table-responsive::-webkit-scrollbar {
height: 8px;
}
.table-responsive::-webkit-scrollbar-track {
background: #f9f9f9;
border-radius: 10px;
}
.table-responsive::-webkit-scrollbar-thumb {
background-color: #ddd;
border-radius: 10px;
}
.select2-container--default .select2-selection--single {
height: 40px;
border-radius: 6px;
border-color: #ddd;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: 40px;
padding-left: 15px;
}
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 38px;
}
/* Animation for loading elements */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translate3d(0, 40px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.animate-fadeInUp {
animation: fadeInUp 0.5s ease-out;
}
/* Print styles */
@media print {
body * {
visibility: hidden;
}
#print-template, #print-template * {
visibility: visible;
}
#print-template {
position: absolute;
left: 0;
top: 0;
width: 100%;
}
}

486
views/reporttt/report.js Normal file
View File

@ -0,0 +1,486 @@
$(document).ready(function() {
// Initialize Select2 for dropdowns
$('.select').select2({
width: '100%',
placeholder: "Pilih opsi...",
allowClear: true
});
// Safe initialization of DataTable - check if it's already initialized
var reportTable;
if (!$.fn.DataTable.isDataTable('#report-table')) {
reportTable = $('#report-table').DataTable({
dom: "<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
buttons: [
{
extend: 'copy',
text: '<i class="fas fa-copy"></i> Copy',
className: 'btn btn-sm btn-secondary'
},
{
extend: 'csv',
text: '<i class="fas fa-file-csv"></i> CSV',
className: 'btn btn-sm btn-secondary'
},
{
extend: 'excel',
text: '<i class="fas fa-file-excel"></i> Excel',
className: 'btn btn-sm btn-success'
},
{
extend: 'pdf',
text: '<i class="fas fa-file-pdf"></i> PDF',
className: 'btn btn-sm btn-danger'
},
{
extend: 'print',
text: '<i class="fas fa-print"></i> Print',
className: 'btn btn-sm btn-primary'
}
],
"pageLength": 10,
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]],
"language": {
"paginate": {
"previous": "<i class='fas fa-chevron-left'></i>",
"next": "<i class='fas fa-chevron-right'></i>"
},
"search": "Search:",
"emptyTable": "Tidak ada data laporan yang tersedia",
"info": "Menampilkan _START_ sampai _END_ dari _TOTAL_ entri",
"infoEmpty": "Menampilkan 0 sampai 0 dari 0 entri",
"infoFiltered": "(difilter dari _MAX_ total entri)",
"lengthMenu": "Tampilkan _MENU_ entri",
"zeroRecords": "Tidak ditemukan data yang sesuai"
},
"order": [[1, 'desc']] // Order by date column descending
});
// Add the DataTable buttons to the custom button container only if not already added
reportTable.buttons().container().appendTo($('.wordset ul'));
} else {
// If already initialized, just get the existing instance
reportTable = $('#report-table').DataTable();
}
// Search functionality
$('#search-input').on('keyup', function() {
reportTable.search(this.value).draw();
});
// Filter Type Change Handler
$('#filter-type').on('change', function() {
const filterType = $(this).val();
$('.filter-option').hide();
if (filterType === 'date_range') {
$('#date-range-filter').show();
} else if (filterType === 'month') {
$('#month-filter').show();
$('#year-filter').show();
} else if (filterType === 'year') {
$('#year-filter').show();
}
});
// Initialize Date Range Picker
if ($('#daterange').length && !$('#daterange').data('daterangepicker')) {
$('#daterange').daterangepicker({
opens: 'left',
autoUpdateInput: true,
locale: {
format: 'DD/MM/YYYY',
applyLabel: 'Terapkan',
cancelLabel: 'Batal',
fromLabel: 'Dari',
toLabel: 'Sampai',
customRangeLabel: 'Kustom',
daysOfWeek: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
monthNames: ['Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember']
},
ranges: {
'Hari Ini': [moment(), moment()],
'Kemarin': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'7 Hari Terakhir': [moment().subtract(6, 'days'), moment()],
'30 Hari Terakhir': [moment().subtract(29, 'days'), moment()],
'Bulan Ini': [moment().startOf('month'), moment().endOf('month')],
'Bulan Lalu': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
}
}, function(start, end, label) {
$('#start_date').val(start.format('YYYY-MM-DD'));
$('#end_date').val(end.format('YYYY-MM-DD'));
});
}
// Handle receipt image modal
$('#receipt-modal').on('show.bs.modal', function (event) {
const button = $(event.relatedTarget);
const imgSrc = button.data('img');
const title = button.data('title');
const modal = $(this);
modal.find('.modal-title').text(title);
modal.find('#receipt-img').attr('src', imgSrc);
// Set download link
$('#download-receipt').off('click').on('click', function() {
const a = document.createElement('a');
a.href = imgSrc;
a.download = 'receipt_' + title.replace('Nota #', '') + '.jpg';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
});
// Print functionality
$('#print-btn, #table-print').off('click').on('click', function() {
printReport();
});
// Export to PDF button
$('#export-pdf').off('click').on('click', function() {
exportReport('pdf');
});
// Export to Excel button
$('#export-excel').off('click').on('click', function() {
exportReport('excel');
});
// Initialize Category Distribution Chart
initCategoryChart();
// Initialize Value Distribution Chart
initValueChart();
// Refresh charts when filter form is submitted
$('#report-filter-form').off('submit').on('submit', function() {
refreshCharts();
return true; // Continue with form submission
});
});
// Function to print the report
function printReport() {
// Set period text
let periodText = '';
const filterType = $('#filter-type').val();
if (filterType === 'date_range') {
periodText = $('#daterange').val();
} else if (filterType === 'month') {
const month = $('select[name="filter_month"] option:selected').text();
const year = $('select[name="filter_year"]').val();
periodText = month + ' ' + year;
} else if (filterType === 'year') {
periodText = 'Tahun ' + $('select[name="filter_year"]').val();
}
$('#print-period').text(periodText);
// Populate table body
const tableBody = $('#print-table-body');
tableBody.empty();
// Get data from the visible table
$('#report-table tbody tr').each(function() {
const cells = $(this).find('td');
// Skip if it's the "no data" row
if (cells.length <= 1) return;
const id = cells.eq(0).text();
const date = cells.eq(1).text();
const product = cells.eq(2).find('a').text();
const category = cells.eq(3).text();
const qty = cells.eq(4).text();
const price = cells.eq(5).text();
const total = cells.eq(6).text();
tableBody.append(`
<tr>
<td style="padding: 8px; border: 1px solid #ddd;">${id}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${date}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${product}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${category}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${qty}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${price}</td>
<td style="padding: 8px; border: 1px solid #ddd;">${total}</td>
</tr>
`);
});
// Open print dialog
const printContent = document.getElementById('print-template').innerHTML;
const printWindow = window.open('', '_blank');
printWindow.document.write(`
<html>
<head>
<title>Print Report</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
h2 {
color: #333;
margin-bottom: 5px;
}
p {
color: #666;
margin-top: 0;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
padding: 8px;
border: 1px solid #ddd;
}
th {
background-color: #f2f2f2;
font-weight: bold;
text-align: left;
}
tfoot td {
font-weight: bold;
background-color: #f9f9f9;
}
</style>
</head>
<body>
${printContent}
</body>
</html>
`);
printWindow.document.close();
setTimeout(function() {
printWindow.print();
printWindow.close();
}, 500);
}
// Function to export report (PDF or Excel)
function exportReport(type) {
// Get current filter values
const filterForm = $('#report-filter-form');
const filterType = $('#filter-type').val();
const startDate = $('#start_date').val();
const endDate = $('#end_date').val();
const filterMonth = $('select[name="filter_month"]').val();
const filterYear = $('select[name="filter_year"]').val();
const productId = $('select[name="product_id"]').val();
const categoryId = $('select[name="category_id"]').val();
// Construct URL with parameters
let url = 'report_export.php?export=' + type;
if (filterType === 'date_range') {
url += '&filter_type=date_range&start_date=' + startDate + '&end_date=' + endDate;
} else if (filterType === 'month') {
url += '&filter_type=month&filter_month=' + filterMonth + '&filter_year=' + filterYear;
} else if (filterType === 'year') {
url += '&filter_type=year&filter_year=' + filterYear;
}
if (productId) {
url += '&product_id=' + productId;
}
if (categoryId) {
url += '&category_id=' + categoryId;
}
// Open in new window/tab
window.open(url, '_blank');
}
// Initialize Category Distribution Chart
function initCategoryChart() {
const categoryCtx = document.getElementById('categoryChart');
if (!categoryCtx) return;
// Destroy existing chart if it exists
if (window.categoryChart) {
window.categoryChart.destroy();
}
window.categoryChart = new Chart(categoryCtx.getContext('2d'), {
type: 'pie',
data: {
labels: [],
datasets: [{
label: 'Jumlah Items',
data: [],
backgroundColor: [
'rgba(255, 99, 132, 0.7)',
'rgba(54, 162, 235, 0.7)',
'rgba(255, 206, 86, 0.7)',
'rgba(75, 192, 192, 0.7)',
'rgba(153, 102, 255, 0.7)',
'rgba(255, 159, 64, 0.7)',
'rgba(199, 199, 199, 0.7)',
'rgba(83, 102, 255, 0.7)',
'rgba(40, 159, 64, 0.7)',
'rgba(210, 199, 199, 0.7)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.raw || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = Math.round((value / total) * 100);
return `${label}: ${value} (${percentage}%)`;
}
}
}
}
}
});
// Load initial data
const labels = [];
const data = [];
// Get data from PHP-rendered elements with special data attributes
const dataElements = document.querySelectorAll('[data-category-name]');
dataElements.forEach(element => {
labels.push(element.getAttribute('data-category-name'));
data.push(parseInt(element.getAttribute('data-category-count')));
});
// If no data elements found, try to parse from the script
if (labels.length === 0) {
try {
// This assumes there's PHP-rendered data in the original script
const categoryData = JSON.parse(document.getElementById('category-data').textContent);
categoryData.forEach(item => {
labels.push(item.name);
data.push(item.count);
});
} catch (e) {
console.warn('Could not load category chart data', e);
}
}
// Update chart with data
window.categoryChart.data.labels = labels;
window.categoryChart.data.datasets[0].data = data;
window.categoryChart.update();
}
// Initialize Value Distribution Chart
function initValueChart() {
const valueCtx = document.getElementById('valueChart');
if (!valueCtx) return;
// Destroy existing chart if it exists
if (window.valueChart) {
window.valueChart.destroy();
}
window.valueChart = new Chart(valueCtx.getContext('2d'), {
type: 'bar',
data: {
labels: [],
datasets: [{
label: 'Nilai (Rp)',
data: [],
backgroundColor: 'rgba(54, 162, 235, 0.6)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return 'Rp ' + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
}
}
},
plugins: {
tooltip: {
callbacks: {
label: function(context) {
let label = context.dataset.label || '';
if (label) {
label += ': ';
}
if (context.parsed.y !== null) {
label += 'Rp ' + context.parsed.y.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
return label;
}
}
}
}
}
});
// Load initial data
const labels = [];
const data = [];
// Get data from PHP-rendered elements with special data attributes
const dataElements = document.querySelectorAll('[data-category-name]');
dataElements.forEach(element => {
labels.push(element.getAttribute('data-category-name'));
data.push(parseInt(element.getAttribute('data-category-amount')));
});
// If no data elements found, try to parse from the script
if (labels.length === 0) {
try {
// This assumes there's PHP-rendered data in the original script
const categoryData = JSON.parse(document.getElementById('category-data').textContent);
categoryData.forEach(item => {
labels.push(item.name);
data.push(item.amount);
});
} catch (e) {
console.warn('Could not load value chart data', e);
}
}
// Update chart with data
window.valueChart.data.labels = labels;
window.valueChart.data.datasets[0].data = data;
window.valueChart.update();
}
// Refresh chart data from server
function refreshCharts() {
// Try to update charts with data present in the page first
try {
initCategoryChart();
initValueChart();
} catch (e) {
console.warn('Could not refresh charts with page data', e);
}
}

1333
views/reporttt/report.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,470 @@
<?php
// Include database connection
require_once '../../routes/db_conn.php';
// Function to generate PDF report
function generatePDF($filters) {
require_once '../vendor/autoload.php'; // Require TCPDF or FPDF library
// Create new PDF document
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// Set document information
$pdf->SetCreator('Ayula Store');
$pdf->SetAuthor('Ayula Store');
$pdf->SetTitle('Laporan Inventaris');
$pdf->SetSubject('Laporan Inventaris');
$pdf->SetKeywords('Laporan, Inventaris, Ayula Store');
// Set default header data
$pdf->SetHeaderData('logo.png', 30, 'Ayula Store', 'Laporan Inventaris', array(0,64,255), array(0,64,128));
$pdf->setFooterData(array(0,64,0), array(0,64,128));
// Set header and footer fonts
$pdf->setHeaderFont(Array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN));
$pdf->setFooterFont(Array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA));
// Set default monospaced font
$pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// Set margins
$pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$pdf->SetHeaderMargin(PDF_MARGIN_HEADER);
$pdf->SetFooterMargin(PDF_MARGIN_FOOTER);
// Set auto page breaks
$pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
// Set image scale factor
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
// Add a page
$pdf->AddPage();
// Set font
$pdf->SetFont('helvetica', '', 10);
// Get report data
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Build query based on filters
$sql = "SELECT r.id_report, r.tanggal, r.jumlah, r.harga, r.image,
b.nama_barang, b.kode_barang,
jb.nama_jenis
FROM report r
JOIN barang b ON r.id_barang = b.id_barang
JOIN jenis_barang jb ON b.id_jenis = jb.id_jenis
WHERE 1=1";
$params = array();
$types = "";
if (!empty($filters['start_date']) && !empty($filters['end_date'])) {
$sql .= " AND DATE(r.tanggal) BETWEEN ? AND ?";
$params[] = $filters['start_date'];
$params[] = $filters['end_date'];
$types .= "ss";
} elseif (!empty($filters['month']) && !empty($filters['year'])) {
$sql .= " AND MONTH(r.tanggal) = ? AND YEAR(r.tanggal) = ?";
$params[] = $filters['month'];
$params[] = $filters['year'];
$types .= "ss";
} elseif (!empty($filters['year'])) {
$sql .= " AND YEAR(r.tanggal) = ?";
$params[] = $filters['year'];
$types .= "s";
}
if (!empty($filters['product_id'])) {
$sql .= " AND b.id_barang = ?";
$params[] = $filters['product_id'];
$types .= "i";
}
if (!empty($filters['category_id'])) {
$sql .= " AND b.id_jenis = ?";
$params[] = $filters['category_id'];
$types .= "i";
}
$sql .= " ORDER BY r.tanggal DESC";
$stmt = $conn->prepare($sql);
if (!empty($params)) {
$stmt->bind_param($types, ...$params);
}
$stmt->execute();
$result = $stmt->get_result();
// Add report title
$title = 'Laporan Inventaris Ayula Store';
$period = '';
if (!empty($filters['start_date']) && !empty($filters['end_date'])) {
$start = date('d M Y', strtotime($filters['start_date']));
$end = date('d M Y', strtotime($filters['end_date']));
$period = "Periode: $start - $end";
} elseif (!empty($filters['month']) && !empty($filters['year'])) {
$month = date('F', mktime(0, 0, 0, $filters['month'], 1));
$period = "Periode: $month {$filters['year']}";
} elseif (!empty($filters['year'])) {
$period = "Periode: Tahun {$filters['year']}";
}
// Add title and period
$pdf->SetFont('helvetica', 'B', 15);
$pdf->Cell(0, 10, $title, 0, 1, 'C');
$pdf->SetFont('helvetica', '', 12);
$pdf->Cell(0, 10, $period, 0, 1, 'C');
$pdf->Ln(5);
// Add summary information
$pdf->SetFont('helvetica', 'B', 12);
$pdf->Cell(0, 10, 'Ringkasan', 0, 1, 'L');
$pdf->SetFont('helvetica', '', 10);
$total_items = 0;
$total_amount = 0;
$reports_count = $result->num_rows;
// Create table headers
$pdf->SetFont('helvetica', 'B', 9);
$pdf->SetFillColor(230, 230, 230);
$pdf->Cell(15, 7, 'ID', 1, 0, 'C', 1);
$pdf->Cell(25, 7, 'Tanggal', 1, 0, 'C', 1);
$pdf->Cell(50, 7, 'Produk', 1, 0, 'C', 1);
$pdf->Cell(30, 7, 'Kategori', 1, 0, 'C', 1);
$pdf->Cell(15, 7, 'Jumlah', 1, 0, 'C', 1);
$pdf->Cell(25, 7, 'Harga', 1, 0, 'C', 1);
$pdf->Cell(30, 7, 'Total', 1, 1, 'C', 1);
// Add data rows
$pdf->SetFont('helvetica', '', 8);
while ($row = $result->fetch_assoc()) {
$total = $row['jumlah'] * $row['harga'];
$total_items += $row['jumlah'];
$total_amount += $total;
$pdf->Cell(15, 6, $row['id_report'], 1, 0, 'C');
$pdf->Cell(25, 6, date('d M Y', strtotime($row['tanggal'])), 1, 0, 'C');
$pdf->Cell(50, 6, $row['nama_barang'], 1, 0, 'L');
$pdf->Cell(30, 6, $row['nama_jenis'], 1, 0, 'L');
$pdf->Cell(15, 6, $row['jumlah'], 1, 0, 'C');
$pdf->Cell(25, 6, 'Rp ' . number_format($row['harga'], 0, ',', '.'), 1, 0, 'R');
$pdf->Cell(30, 6, 'Rp ' . number_format($total, 0, ',', '.'), 1, 1, 'R');
}
// Add totals row
$pdf->SetFont('helvetica', 'B', 9);
$pdf->Cell(120, 7, 'TOTAL', 1, 0, 'R', 1);
$pdf->Cell(15, 7, $total_items, 1, 0, 'C', 1);
$pdf->Cell(25, 7, '', 1, 0, 'C', 1);
$pdf->Cell(30, 7, 'Rp ' . number_format($total_amount, 0, ',', '.'), 1, 1, 'R', 1);
// Add summary before table
$pdf->SetY(60); // Position at beginning
$pdf->SetFont('helvetica', '', 10);
$pdf->Cell(60, 7, 'Total Laporan: ' . $reports_count, 0, 1, 'L');
$pdf->Cell(60, 7, 'Total Items: ' . $total_items, 0, 1, 'L');
$pdf->Cell(60, 7, 'Total Nilai: Rp ' . number_format($total_amount, 0, ',', '.'), 0, 1, 'L');
$pdf->Ln(5);
// Close and output PDF
$pdf->Output('Laporan_Inventaris_' . date('Y-m-d') . '.pdf', 'I');
$stmt->close();
$conn->close();
}
// Function to export to Excel
function exportExcel($filters) {
require_once '../vendor/autoload.php'; // Require PhpSpreadsheet
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
// Create new Spreadsheet object
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Set document properties
$spreadsheet->getProperties()
->setCreator('Ayula Store')
->setLastModifiedBy('Ayula Store')
->setTitle('Laporan Inventaris')
->setSubject('Laporan Inventaris Ayula Store')
->setDescription('Laporan Inventaris Ayula Store');
// Get report data
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Build query based on filters
$sql = "SELECT r.id_report, r.tanggal, r.jumlah, r.harga, r.image,
b.nama_barang, b.kode_barang,
jb.nama_jenis
FROM report r
JOIN barang b ON r.id_barang = b.id_barang
JOIN jenis_barang jb ON b.id_jenis = jb.id_jenis
WHERE 1=1";
$params = array();
$types = "";
if (!empty($filters['start_date']) && !empty($filters['end_date'])) {
$sql .= " AND DATE(r.tanggal) BETWEEN ? AND ?";
$params[] = $filters['start_date'];
$params[] = $filters['end_date'];
$types .= "ss";
} elseif (!empty($filters['month']) && !empty($filters['year'])) {
$sql .= " AND MONTH(r.tanggal) = ? AND YEAR(r.tanggal) = ?";
$params[] = $filters['month'];
$params[] = $filters['year'];
$types .= "ss";
} elseif (!empty($filters['year'])) {
$sql .= " AND YEAR(r.tanggal) = ?";
$params[] = $filters['year'];
$types .= "s";
}
if (!empty($filters['product_id'])) {
$sql .= " AND b.id_barang = ?";
$params[] = $filters['product_id'];
$types .= "i";
}
if (!empty($filters['category_id'])) {
$sql .= " AND b.id_jenis = ?";
$params[] = $filters['category_id'];
$types .= "i";
}
$sql .= " ORDER BY r.tanggal DESC";
$stmt = $conn->prepare($sql);
if (!empty($params)) {
$stmt->bind_param($types, ...$params);
}
$stmt->execute();
$result = $stmt->get_result();
// Add title
$sheet->setCellValue('A1', 'LAPORAN INVENTARIS AYULA STORE');
$sheet->mergeCells('A1:G1');
$sheet->getStyle('A1')->getFont()->setBold(true)->setSize(14);
$sheet->getStyle('A1')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Add period
$period = '';
if (!empty($filters['start_date']) && !empty($filters['end_date'])) {
$start = date('d M Y', strtotime($filters['start_date']));
$end = date('d M Y', strtotime($filters['end_date']));
$period = "Periode: $start - $end";
} elseif (!empty($filters['month']) && !empty($filters['year'])) {
$month = date('F', mktime(0, 0, 0, $filters['month'], 1));
$period = "Periode: $month {$filters['year']}";
} elseif (!empty($filters['year'])) {
$period = "Periode: Tahun {$filters['year']}";
}
$sheet->setCellValue('A2', $period);
$sheet->mergeCells('A2:G2');
$sheet->getStyle('A2')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Add summary information
$total_items = 0;
$total_amount = 0;
$reports_count = $result->num_rows;
// Calculate totals
$data = [];
while ($row = $result->fetch_assoc()) {
$data[] = $row;
$total_items += $row['jumlah'];
$total_amount += ($row['jumlah'] * $row['harga']);
}
// Add summary section
$sheet->setCellValue('A4', 'Ringkasan:');
$sheet->getStyle('A4')->getFont()->setBold(true);
$sheet->setCellValue('A5', 'Total Laporan:');
$sheet->setCellValue('B5', $reports_count);
$sheet->setCellValue('A6', 'Total Items:');
$sheet->setCellValue('B6', $total_items);
$sheet->setCellValue('A7', 'Total Nilai:');
$sheet->setCellValue('B7', 'Rp ' . number_format($total_amount, 0, ',', '.'));
// Add table headers
$sheet->setCellValue('A9', 'ID');
$sheet->setCellValue('B9', 'Tanggal');
$sheet->setCellValue('C9', 'Produk');
$sheet->setCellValue('D9', 'Kategori');
$sheet->setCellValue('E9', 'Jumlah');
$sheet->setCellValue('F9', 'Harga');
$sheet->setCellValue('G9', 'Total');
// Style the headers
$sheet->getStyle('A9:G9')->getFont()->setBold(true);
$sheet->getStyle('A9:G9')->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setRGB('CCCCCC');
$sheet->getStyle('A9:G9')->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
// Add data rows
$row_num = 10;
foreach ($data as $row) {
$total = $row['jumlah'] * $row['harga'];
$sheet->setCellValue('A' . $row_num, $row['id_report']);
$sheet->setCellValue('B' . $row_num, date('d M Y', strtotime($row['tanggal'])));
$sheet->setCellValue('C' . $row_num, $row['nama_barang']);
$sheet->setCellValue('D' . $row_num, $row['nama_jenis']);
$sheet->setCellValue('E' . $row_num, $row['jumlah']);
$sheet->setCellValue('F' . $row_num, 'Rp ' . number_format($row['harga'], 0, ',', '.'));
$sheet->setCellValue('G' . $row_num, 'Rp ' . number_format($total, 0, ',', '.'));
$row_num++;
}
// Add totals row
$sheet->setCellValue('A' . $row_num, 'TOTAL');
$sheet->mergeCells('A' . $row_num . ':D' . $row_num);
$sheet->setCellValue('E' . $row_num, $total_items);
$sheet->setCellValue('G' . $row_num, 'Rp ' . number_format($total_amount, 0, ',', '.'));
$sheet->getStyle('A' . $row_num . ':G' . $row_num)->getFont()->setBold(true);
$sheet->getStyle('A' . $row_num . ':G' . $row_num)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setRGB('EEEEEE');
// Autosize columns
foreach(range('A', 'G') as $col) {
$sheet->getColumnDimension($col)->setAutoSize(true);
}
// Create a border for the table
$styleArray = [
'borders' => [
'allBorders' => [
'borderStyle' => \PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN,
],
],
];
$sheet->getStyle('A9:G' . $row_num)->applyFromArray($styleArray);
// Output excel file
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="Laporan_Inventaris_' . date('Y-m-d') . '.xlsx"');
header('Cache-Control: max-age=0');
$writer = new Xlsx($spreadsheet);
$writer->save('php://output');
$stmt->close();
$conn->close();
exit();
}
// Process report export request
if (isset($_GET['export']) && in_array($_GET['export'], ['pdf', 'excel'])) {
$filters = [
'start_date' => isset($_GET['start_date']) ? $_GET['start_date'] : '',
'end_date' => isset($_GET['end_date']) ? $_GET['end_date'] : '',
'month' => isset($_GET['filter_month']) ? $_GET['filter_month'] : '',
'year' => isset($_GET['filter_year']) ? $_GET['filter_year'] : '',
'product_id' => isset($_GET['product_id']) ? $_GET['product_id'] : '',
'category_id' => isset($_GET['category_id']) ? $_GET['category_id'] : ''
];
if ($_GET['export'] == 'pdf') {
generatePDF($filters);
} else {
exportExcel($filters);
}
exit();
}
// Handle AJAX request for chart data
if (isset($_GET['action']) && $_GET['action'] == 'get_chart_data') {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die(json_encode(['error' => 'Connection failed: ' . $conn->connect_error]));
}
// Build query based on filters
$sql = "SELECT jb.nama_jenis, SUM(r.jumlah) as total_items, SUM(r.jumlah * r.harga) as total_value
FROM report r
JOIN barang b ON r.id_barang = b.id_barang
JOIN jenis_barang jb ON b.id_jenis = jb.id_jenis
WHERE 1=1";
$params = array();
$types = "";
if (!empty($_GET['start_date']) && !empty($_GET['end_date'])) {
$sql .= " AND DATE(r.tanggal) BETWEEN ? AND ?";
$params[] = $_GET['start_date'];
$params[] = $_GET['end_date'];
$types .= "ss";
} elseif (!empty($_GET['filter_month']) && !empty($_GET['filter_year'])) {
$sql .= " AND MONTH(r.tanggal) = ? AND YEAR(r.tanggal) = ?";
$params[] = $_GET['filter_month'];
$params[] = $_GET['filter_year'];
$types .= "ss";
} elseif (!empty($_GET['filter_year'])) {
$sql .= " AND YEAR(r.tanggal) = ?";
$params[] = $_GET['filter_year'];
$types .= "s";
}
if (!empty($_GET['product_id'])) {
$sql .= " AND b.id_barang = ?";
$params[] = $_GET['product_id'];
$types .= "i";
}
if (!empty($_GET['category_id'])) {
$sql .= " AND b.id_jenis = ?";
$params[] = $_GET['category_id'];
$types .= "i";
}
$sql .= " GROUP BY jb.nama_jenis";
$stmt = $conn->prepare($sql);
if (!empty($params)) {
$stmt->bind_param($types, ...$params);
}
$stmt->execute();
$result = $stmt->get_result();
$chart_data = [
'labels' => [],
'item_counts' => [],
'values' => []
];
while ($row = $result->fetch_assoc()) {
$chart_data['labels'][] = $row['nama_jenis'];
$chart_data['item_counts'][] = (int)$row['total_items'];
$chart_data['values'][] = (float)$row['total_value'];
}
echo json_encode($chart_data);
$stmt->close();
$conn->close();
exit();
}

View File

@ -0,0 +1,160 @@
/* Barcode Scanner Styles */
.barcode-scanner-container {
position: relative;
z-index: 100;
background: linear-gradient(45deg, #ff9f4380, #ff7f5080);
border-radius: 10px;
padding: 15px;
margin-bottom: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.barcode-scanner-container.sticky-scanner {
position: fixed;
left: 0;
right: 0;
margin: 0 auto;
width: calc(100% - 30px);
max-width: 1140px;
border-radius: 0 0 10px 10px;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
padding: 10px 15px;
z-index: 1000;
}
.barcode-scanner-container.collapsed {
padding: 5px 15px;
}
.barcode-scanner-container.collapsed .barcode-content {
display: none;
}
.barcode-scanner-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
cursor: pointer;
}
.barcode-scanner-container.collapsed .barcode-scanner-header {
margin-bottom: 0;
}
.barcode-scanner-header h5 {
margin: 0;
color: #fff;
font-weight: 600;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
}
.barcode-toggle-btn {
background: rgba(255, 255, 255, 0.3);
color: #fff;
border: none;
border-radius: 5px;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.barcode-content {
position: relative;
}
.barcode-input-group {
display: flex;
align-items: center;
background: #fff;
border-radius: 5px;
overflow: hidden;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
}
.barcode-input-group .input-group-text {
background-color: #f8f9fa;
border: none;
padding: 10px 15px;
}
#barcode-input {
border: none;
box-shadow: none;
padding: 10px 15px;
font-size: 16px;
background-color: #fff;
flex-grow: 1;
}
#barcode-input:focus {
outline: none;
}
#barcode-status {
margin-top: 8px;
font-size: 14px;
color: #6c757d;
height: 20px;
}
/* Scanning animation */
.barcode-scanner-container.scanning {
background: linear-gradient(45deg, #ff9f4330, #ff7f5030);
}
.barcode-scanner-container.scanning .barcode-input-group {
box-shadow: 0 0 0 2px #ff9f43;
}
/* Highlight product in grid */
.productset.highlight-product {
border: 2px solid #28a745;
box-shadow: 0 0 15px rgba(40, 167, 69, 0.5);
animation: highlight-pulse 1.5s;
}
@keyframes highlight-pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 rgba(40, 167, 69, 0.5);
}
50% {
transform: scale(1.05);
box-shadow: 0 0 15px rgba(40, 167, 69, 0.8);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 rgba(40, 167, 69, 0.5);
}
}
/* Product Added Notification */
.barcode-notification {
position: fixed;
top: 70px;
right: 20px;
background-color: #28a745;
color: white;
padding: 10px 15px;
border-radius: 5px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
z-index: 9999;
opacity: 0;
transform: translateX(50px);
transition: opacity 0.3s, transform 0.3s;
font-weight: bold;
max-width: 280px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.barcode-notification.show {
opacity: 1;
transform: translateX(0);
}

View File

@ -0,0 +1,453 @@
/**
* Barcode Scanner Feature
* Adds barcode scanning functionality to Ayula Store POS System
*/
$(document).ready(function() {
// Initialize barcode scanner functionality
initBarcodeScanner();
// Check for barcode scanner toggle
setupBarcodeScannerToggle();
// Force update the clear cart button state
forceUpdateClearCartButton();
});
/**
* Force update the clear cart button state based on actual cart content
*/
function forceUpdateClearCartButton() {
// Check if cart actually has items
const hasItems = $('.product-lists').length > 0;
console.log('Force updating clear cart button. Cart has items:', hasItems);
const clearCartBtn = $('#clear-cart-btn');
if (hasItems) {
clearCartBtn.attr('href', 'javascript:void(0);')
.removeClass('disabled')
.css({
'opacity': '1',
'cursor': 'pointer'
})
.off('click')
.on('click', function(e) {
e.preventDefault();
if (typeof bootstrap !== 'undefined' && $('#clearCartModal').length) {
try {
const clearModal = new bootstrap.Modal(document.getElementById('clearCartModal'));
clearModal.show();
} catch (error) {
console.log('Modal error, using fallback:', error);
if (confirm('Apakah Anda yakin ingin menghapus semua item dari keranjang Anda?')) {
window.location.href = 'index.php?clear_cart=1';
}
}
} else {
if (confirm('Apakah Anda yakin ingin menghapus semua item dari keranjang Anda?')) {
window.location.href = 'index.php?clear_cart=1';
}
}
});
} else {
clearCartBtn.attr('href', '#')
.addClass('disabled')
.css({
'opacity': '0.5',
'cursor': 'not-allowed'
})
.off('click');
}
}
/**
* Initialize barcode scanner functionality
*/
function initBarcodeScanner() {
const barcodeInput = $('#barcode-input');
// Focus barcode input when barcode scanner container is clicked
$('.barcode-scanner-container').on('click', function(e) {
// Don't focus if clicking on a button or another interactive element
if (!$(e.target).is('button, a, input')) {
barcodeInput.focus();
}
});
// Make barcode scanner sticky at top
makeBarcodeSticky();
// Handle barcode input
barcodeInput.on('keydown', function(e) {
// If Enter key is pressed, process the barcode
if (e.keyCode === 13) {
e.preventDefault();
const barcode = $(this).val().trim();
if (barcode) {
processBarcode(barcode);
$(this).val(''); // Clear input after processing
}
}
});
// Auto focus the barcode input when page loads
setTimeout(function() {
barcodeInput.focus();
}, 500);
// Handle window focus to automatically focus barcode input
$(window).on('focus', function() {
// Only focus if scanner is not collapsed
if (!$('.barcode-scanner-container').hasClass('collapsed')) {
setTimeout(function() {
barcodeInput.focus();
}, 100);
}
});
}
/**
* Make barcode scanner sticky at top of page
*/
function makeBarcodeSticky() {
const barcodeContainer = $('.barcode-scanner-container');
if (!barcodeContainer.length) return;
const originalPosition = barcodeContainer.offset() ? barcodeContainer.offset().top : 0;
const headerHeight = $('.header').outerHeight() || 0;
$(window).on('scroll', function() {
const scrollPosition = $(window).scrollTop();
if (scrollPosition > originalPosition - headerHeight) {
barcodeContainer.addClass('sticky-scanner');
barcodeContainer.css('top', headerHeight + 'px');
} else {
barcodeContainer.removeClass('sticky-scanner');
barcodeContainer.css('top', '');
}
});
}
/**
* Setup toggle for barcode scanner container
*/
function setupBarcodeScannerToggle() {
$('#toggle-barcode-scanner').on('click', function() {
$('.barcode-scanner-container').toggleClass('collapsed');
$(this).find('i').toggleClass('fa-chevron-up fa-chevron-down');
// Focus the input when expanded
if (!$('.barcode-scanner-container').hasClass('collapsed')) {
$('#barcode-input').focus();
}
});
}
/**
* Process barcode and add product to cart
*/
function processBarcode(barcode) {
// Show loading indicator
$('.barcode-scanner-container').addClass('scanning');
$('#barcode-status').html('<i class="fa fa-spinner fa-spin"></i> Memindai...');
// Keep focus on the barcode input field
const barcodeInput = $('#barcode-input');
// Send AJAX request to get product by barcode
$.ajax({
url: 'get_product_by_barcode.php',
type: 'POST',
data: {
barcode: barcode
},
dataType: 'json',
success: function(response) {
$('.barcode-scanner-container').removeClass('scanning');
if (response.success) {
// Show success message
$('#barcode-status').html(
'<div class="text-success"><i class="fa fa-check-circle"></i> Ditambahkan: ' +
response.product.name + '</div>'
);
// Highlight the product in the grid if visible (without scrolling)
highlightProductInGrid(response.product.id);
// Update cart section via AJAX
if (response.cart_html) {
// Update the cart HTML
$('.product-table').html(response.cart_html);
// Update cart totals and related UI elements
updateAllCartElements(response.cart_totals);
// Make sure quantity controls and other event handlers are properly set up
if (typeof setupQuantityControls === 'function') {
setupQuantityControls();
}
if (typeof setupCartDeletionConfirmation === 'function') {
setupCartDeletionConfirmation();
}
// Force update the clear cart button
forceUpdateClearCartButton();
// Trigger custom event for other scripts to respond to cart updates
document.dispatchEvent(new CustomEvent('cartUpdated'));
}
} else {
// Show error message
$('#barcode-status').html(
'<div class="text-danger"><i class="fa fa-exclamation-circle"></i> ' +
response.message + '</div>'
);
}
// Reset status message after delay
setTimeout(function() {
$('#barcode-status').html('<i class="fa fa-barcode"></i> Pindai barcode');
// Restore focus to barcode input
barcodeInput.focus();
// Try to restore cursor position
if (barcodeInput.get(0) && barcodeInput.get(0).setSelectionRange) {
barcodeInput.get(0).setSelectionRange(0, 0);
}
}, 3000);
},
error: function(xhr, status, error) {
$('.barcode-scanner-container').removeClass('scanning');
console.error('AJAX Error:', status, error);
// Try to get more detailed error info
let errorMessage = 'Koneksi error';
try {
if (xhr.responseText) {
// Check if it's JSON
try {
const errorData = JSON.parse(xhr.responseText);
if (errorData && errorData.message) {
errorMessage = errorData.message;
}
} catch (jsonError) {
// Not valid JSON, just use generic message
errorMessage = 'Kesalahan sistem. Silakan coba lagi.';
console.log('Response was not valid JSON:', xhr.responseText.substring(0, 100));
}
}
} catch (e) {
console.error('Error parsing error response:', e);
}
$('#barcode-status').html(
'<div class="text-danger"><i class="fa fa-exclamation-circle"></i> ' + errorMessage + '</div>'
);
// Reset status message after delay
setTimeout(function() {
$('#barcode-status').html('<i class="fa fa-barcode"></i> Pindai barcode');
barcodeInput.focus();
}, 3000);
}
});
}
/**
* Comprehensive update of all cart-related elements
*/
function updateAllCartElements(totals) {
console.log('Updating all cart elements with totals:', totals);
// 1. Update displayed values
$('.totalitem h4').text('Total barang: ' + totals.items);
$('.total-value h6').text('Rp. ' + totals.formatted_total);
$('.btn-totallabel h6').text('Rp. ' + totals.formatted_total);
// 2. Update form inputs
$('#cash-amount').val(totals.formatted_total);
$('#hidden-cash-amount').val(totals.total);
// 3. Create and replace checkout form if doesn't exist or needs updating
if (totals.items > 0) {
// Only replace if needed
if ($('#checkout-form').length === 0 || $('#checkout-form button[type="submit"]').prop('disabled')) {
const checkoutFormHtml = `
<form id="checkout-form" method="post" action="index.php">
<input type="hidden" name="cash_amount" id="hidden-cash-amount" value="${totals.total}">
<input type="hidden" name="change_amount" id="hidden-change-amount" value="0">
<input type="hidden" name="checkout" value="1">
<button type="submit" class="btn-totallabel w-100">
<h5>Pesan Sekarang</h5>
<h6>Rp. ${totals.formatted_total}</h6>
</button>
</form>
`;
// Replace current form with new one
if ($('#checkout-form').length > 0) {
$('#checkout-form').replaceWith(checkoutFormHtml);
} else {
// If form doesn't exist, replace the disabled button
$('.btn-totallabel.w-100[disabled]').replaceWith(checkoutFormHtml);
}
// Setup the new form
if (typeof setupCheckoutForm === 'function') {
setupCheckoutForm();
}
} else {
// Just enable the existing button
$('.btn-totallabel').prop('disabled', false);
$('#checkout-form button[type="submit"]').prop('disabled', false);
}
} else {
// Cart is empty, show disabled button
if ($('#checkout-form').length > 0) {
$('#checkout-form').replaceWith(`
<button class="btn-totallabel w-100" disabled>
<h5>Pesan Sekarang</h5>
<h6>Rp. 0</h6>
</button>
`);
}
}
// 4. Update quick cash buttons
updateQuickCashButtons(totals.total);
// 5. Show/hide change display
updateChangeDisplay();
// 6. Re-initialize scrollbars and event handlers
initializeCartScrolling();
}
/**
* Initialize scrolling behavior for cart
*/
function initializeCartScrolling() {
// Make sure product table has correct max height
const windowHeight = $(window).height();
const headerHeight = $('.order-list').outerHeight() || 0;
const cardHeaderHeight = $('.card-order .card-body:first-of-type').outerHeight() || 0;
const cardFooterHeight = $('.card-order .card-body:last-of-type').outerHeight() || 0;
const availableHeight = windowHeight - headerHeight - cardHeaderHeight - cardFooterHeight - 50; // 50px buffer
// Set max height for scrollable area
$('.product-table').css('max-height', availableHeight + 'px');
}
/**
* Highlight product in grid when added via barcode
*/
function highlightProductInGrid(productId) {
const productElement = $('.productset[data-product-id="' + productId + '"]');
if (productElement.length) {
// Flash highlight effect without scrolling
productElement.addClass('highlight-product');
setTimeout(function() {
productElement.removeClass('highlight-product');
}, 1500);
// Optional: Briefly show which product was added via a popup notification
showAddedProductNotification(productElement.find('.productsetcontent h4').text());
}
}
/**
* Show a notification that a product was added instead of scrolling
*/
function showAddedProductNotification(productName) {
// Create notification element if it doesn't exist
if ($('#barcode-notification').length === 0) {
$('body').append('<div id="barcode-notification" class="barcode-notification"></div>');
}
// Show notification
$('#barcode-notification')
.text('Ditambahkan: ' + productName)
.addClass('show');
// Hide notification after 2 seconds
setTimeout(function() {
$('#barcode-notification').removeClass('show');
}, 2000);
}
/**
* Update quick cash buttons based on total amount
*/
function updateQuickCashButtons(totalAmount) {
if (totalAmount > 0) {
// Update round-up buttons for different denominations
const round1000 = Math.ceil(totalAmount / 1000) * 1000;
const round10000 = Math.ceil(totalAmount / 10000) * 10000;
const round50000 = Math.ceil(totalAmount / 50000) * 50000;
const round100000 = Math.ceil(totalAmount / 100000) * 100000;
// Find quick cash buttons and update their values
$('.quick-cash').each(function(index) {
let newValue;
switch(index) {
case 0: newValue = round1000; break;
case 1: newValue = round10000; break;
case 2: newValue = round50000; break;
case 3: newValue = round100000; break;
default: newValue = totalAmount;
}
$(this).data('value', newValue);
$(this).text('Rp. ' + formatNumber(newValue));
});
// Show row containing quick cash buttons
$('.setvaluecash .row').show();
} else {
// Hide quick cash buttons if cart is empty
$('.setvaluecash .row').hide();
}
}
/**
* Update change display based on current cash amount and total
*/
function updateChangeDisplay() {
const cashInput = $('#cash-amount');
const changeDisplay = $('#change-amount');
const changeContainer = $('#change-container');
const hiddenCashAmount = $('#hidden-cash-amount');
const hiddenChangeAmount = $('#hidden-change-amount');
// Get total from the displayed value or use 0 if not available
const totalText = $('.total-value h6').text();
const totalAmount = parseFloat(totalText.replace(/[^\d]/g, '')) || 0;
// Get cash value from hidden field
let cashValue = parseFloat(hiddenCashAmount.val()) || 0;
// Calculate change
const change = cashValue - totalAmount;
// Update hidden field for change amount
hiddenChangeAmount.val(Math.max(0, change));
// Format and display
if (change >= 0 && totalAmount > 0) {
changeDisplay.text('Rp. ' + formatNumber(change));
changeContainer.show();
} else {
changeContainer.hide();
}
}
/**
* Format number with thousand separator
*/
function formatNumber(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}

View File

@ -0,0 +1,223 @@
/**
* Ayula Store POS System - Cart Functionality
* Handles cart operations and user interactions for shopping cart
*/
$(document).ready(function() {
// Setup quantity controls for cart items
setupQuantityControls();
// Setup checkout form functionality
setupCheckoutForm();
// Setup cart item deletion confirmation
setupCartDeletionConfirmation();
// Setup clear cart confirmation
setupClearCartConfirmation();
// Setup cash amount input formatting
setupCashAmountFormatting();
});
/**
* Set up quantity controls (increment, decrement, manual input) for cart items
*/
function setupQuantityControls() {
// Handle increment button
$('.quantity-btn.increment').off('click').on('click', function() {
const input = $(this).siblings('.quantity-field');
const currentValue = parseInt(input.val()) || 1;
const maxStock = parseInt(input.data('max-stock')) || 999;
if (currentValue < maxStock) {
input.val(currentValue + 1);
}
});
// Handle decrement button
$('.quantity-btn.decrement').off('click').on('click', function() {
const input = $(this).siblings('.quantity-field');
const currentValue = parseInt(input.val()) || 1;
if (currentValue > 1) {
input.val(currentValue - 1);
}
});
// Validate manual input in quantity field
$('.quantity-field').off('change keyup').on('change keyup', function() {
let value = parseInt($(this).val()) || 1;
const maxStock = parseInt($(this).data('max-stock')) || 999;
// Ensure value is within limits
if (value < 1) value = 1;
if (value > maxStock) value = maxStock;
$(this).val(value);
});
}
/**
* Set up checkout form validation and submission
*/
function setupCheckoutForm() {
$('#checkout-form').off('submit').on('submit', function(e) {
// Get the cash amount and total
const cashAmount = parseFloat($('#hidden-cash-amount').val()) || 0;
const totalText = $('.total-value h6').text();
const totalAmount = parseFloat(totalText.replace(/[^\d]/g, '')) || 0;
// Validate that cash amount is sufficient
if (cashAmount < totalAmount) {
e.preventDefault();
alert('Jumlah tunai tidak mencukupi. Mohon masukkan nilai yang sama atau lebih besar dari total.');
return false;
}
// All good, allow form submission
return true;
});
}
/**
* Setup confirmation dialog when removing items from cart
*/
function setupCartDeletionConfirmation() {
$('.delete-cart-item').off('click').on('click', function(e) {
e.preventDefault();
const index = $(this).data('index');
// Set up the confirmation link to the correct index
$('#confirm-delete-btn').attr('href', 'index.php?remove_item=' + index);
// Show modal if available, otherwise use confirm dialog
if (typeof bootstrap !== 'undefined' && $('#deleteConfirmModal').length) {
try {
const modal = new bootstrap.Modal(document.getElementById('deleteConfirmModal'));
modal.show();
} catch (error) {
console.error('Modal error, using fallback:', error);
if (confirm('Apakah Anda yakin ingin menghapus item ini dari keranjang?')) {
window.location.href = 'index.php?remove_item=' + index;
}
}
} else {
if (confirm('Apakah Anda yakin ingin menghapus item ini dari keranjang?')) {
window.location.href = 'index.php?remove_item=' + index;
}
}
});
}
/**
* Setup clear cart confirmation
*/
function setupClearCartConfirmation() {
// This is handled in another part of the code (index.php inline script)
// Just making sure all confirmations are set up correctly
// Clear Cart confirmation - Confirm button handler
$('#confirm-clear-cart').off('click').on('click', function(e) {
window.location.href = 'index.php?clear_cart=1';
});
// Clear Cart confirmation - Cancel button handler
$('#cancel-clear-cart').off('click').on('click', function() {
if (typeof bootstrap !== 'undefined') {
try {
const clearModal = bootstrap.Modal.getInstance(document.getElementById('clearCartModal'));
if (clearModal) {
clearModal.hide();
}
} catch (error) {
console.error('Modal hide error:', error);
// Force modal to close
$('#clearCartModal').modal('hide');
}
}
});
}
/**
* Setup cash amount input formatting with thousand separator
*/
function setupCashAmountFormatting() {
const cashInput = $('#cash-amount');
const hiddenCashAmount = $('#hidden-cash-amount');
const hiddenChangeAmount = $('#hidden-change-amount');
const changeDisplay = $('#change-amount');
const changeContainer = $('#change-container');
// Format the input with thousand separator on change
cashInput.on('input', function() {
// Remove non-numeric characters
let value = $(this).val().replace(/[^\d]/g, '');
// Get the total amount
const totalText = $('.total-value h6').text();
const totalAmount = parseFloat(totalText.replace(/[^\d]/g, '')) || 0;
// Convert to number and format with thousand separator
if (value !== '') {
const numericValue = parseInt(value);
// Calculate change
const change = numericValue - totalAmount;
// Update hidden fields for form submission
hiddenCashAmount.val(numericValue);
hiddenChangeAmount.val(Math.max(0, change));
// Format display value
$(this).val(formatNumber(numericValue));
// Show change if payment is sufficient
if (change >= 0) {
changeDisplay.text('Rp. ' + formatNumber(change));
changeContainer.show();
} else {
changeContainer.hide();
}
} else {
$(this).val('');
changeContainer.hide();
}
});
// Quick cash buttons
$('.quick-cash').on('click', function() {
const value = $(this).data('value');
cashInput.val(formatNumber(value));
// Calculate change
const totalText = $('.total-value h6').text();
const totalAmount = parseFloat(totalText.replace(/[^\d]/g, '')) || 0;
const change = value - totalAmount;
// Update hidden fields for form submission
hiddenCashAmount.val(value);
hiddenChangeAmount.val(Math.max(0, change));
// Show change
if (change >= 0) {
changeDisplay.text('Rp. ' + formatNumber(change));
changeContainer.show();
} else {
changeContainer.hide();
}
});
// Trigger input event to initialize on page load
cashInput.trigger('input');
}
/**
* Format number with thousand separator
*
* @param {number} number The number to format
* @return {string} Formatted number with thousand separator
*/
function formatNumber(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}

View File

@ -0,0 +1,484 @@
<?php
// Start session if not already started
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Include the main database connection
include('../../routes/db_conn.php');
// Check for database connection
if (!isset($conn) || $conn->connect_error) {
error_log("Database connection failed: " . ($conn ? $conn->connect_error : "Connection not established"));
die("Database connection failed. Please try again later.");
}
// Set error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('log_errors', 1);
ini_set('error_log', '../../logs/php-errors.log');
// Check if user is logged in, redirect to login page if not
if (!isset($_SESSION['user_id']) || !isset($_SESSION['role'])) {
// Check if this is an AJAX request
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
// Return JSON error for AJAX requests
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => 'Session expired. Please login again.', 'redirect' => '/ayula-store/views/login/']);
exit;
} else {
// Redirect for regular requests
header('Location: /ayula-store/views/login/');
exit;
}
}
// Set variables for use throughout the application
$userRole = $_SESSION['role'] ?? ''; // 'user' or 'admin'
$username = $_SESSION['username'] ?? 'Unknown User';
$isAdmin = ($userRole === 'admin');
/**
* Get all products or filter by product type and/or search query
*
* @param int|null $typeId Optional product type ID to filter by
* @param string|null $searchQuery Optional search term to filter product names
* @return array List of products
*/
function getProducts($typeId = null, $searchQuery = null) {
global $conn;
try {
$sql = "SELECT b.*, j.nama_jenis
FROM barang_kasir b
JOIN jenis_barang j ON b.id_jenis = j.id_jenis
WHERE 1=1";
$params = [];
$types = "";
// Add type filter if specified
if ($typeId) {
$sql .= " AND b.id_jenis = ?";
$params[] = $typeId;
$types .= "i"; // integer parameter
}
// Add search filter if specified
if ($searchQuery && !empty($searchQuery)) {
$sql .= " AND (b.nama_barang LIKE ? OR b.kode_barang LIKE ?)";
$params[] = "%" . $searchQuery . "%";
$params[] = "%" . $searchQuery . "%";
$types .= "ss"; // two string parameters
}
$sql .= " ORDER BY b.nama_barang ASC";
$stmt = mysqli_prepare($conn, $sql);
if (!$stmt) {
error_log("Query preparation failed: " . mysqli_error($conn));
return [];
}
// Bind parameters dynamically if we have any
if (!empty($params)) {
$bindParams = array(&$stmt, &$types);
foreach($params as $key => $value) {
$bindParams[] = &$params[$key];
}
call_user_func_array('mysqli_stmt_bind_param', $bindParams);
}
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
error_log("Query execution failed: " . mysqli_stmt_error($stmt));
mysqli_stmt_close($stmt);
return [];
}
$result = mysqli_stmt_get_result($stmt);
$products = [];
while ($row = mysqli_fetch_assoc($result)) {
$products[] = $row;
}
mysqli_stmt_close($stmt);
return $products;
} catch (Exception $e) {
error_log("Exception in getProducts: " . $e->getMessage());
return [];
}
}
/**
* Get product details by ID
*
* @param int $productId The product ID
* @return array|null Product details or null if not found
*/
function getProductById($productId) {
global $conn;
try {
$stmt = mysqli_prepare($conn, "SELECT b.*, j.nama_jenis
FROM barang_kasir b
LEFT JOIN jenis_barang j ON b.id_jenis = j.id_jenis
WHERE b.id_barangK = ?");
if (!$stmt) {
error_log("Query preparation failed: " . mysqli_error($conn));
return null;
}
mysqli_stmt_bind_param($stmt, "i", $productId);
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
error_log("Query execution failed: " . mysqli_stmt_error($stmt));
mysqli_stmt_close($stmt);
return null;
}
$result = mysqli_stmt_get_result($stmt);
$product = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
return $product;
} catch (Exception $e) {
error_log("Exception in getProductById: " . $e->getMessage());
return null;
}
}
/**
* Get all product types (jenis_barang)
*
* @return array List of product types
*/
function getProductTypes() {
global $conn;
try {
$query = "SELECT * FROM jenis_barang ORDER BY nama_jenis";
$result = mysqli_query($conn, $query);
if (!$result) {
error_log("Query failed: " . mysqli_error($conn));
return [];
}
$types = [];
while ($row = mysqli_fetch_assoc($result)) {
$types[] = $row;
}
return $types;
} catch (Exception $e) {
error_log("Exception in getProductTypes: " . $e->getMessage());
return [];
}
}
/**
* Create a new transaction
*
* @param array $items Array of items with product_id and quantity
* @param float $total Total amount
* @param float $cashAmount Cash amount received from customer
* @param float $changeAmount Change amount to be returned to customer
* @return array Result with success status and transaction ID
*/
function createTransaction($items, $total, $cashAmount = 0, $changeAmount = 0) {
global $conn;
if (empty($items)) {
return [
'success' => false,
'message' => 'No items in cart'
];
}
$totalItems = array_sum(array_column($items, 'quantity'));
// Generate transaction code (e.g., TRX-20250416-001)
$transactionCode = 'TRX-' . date('Ymd') . '-' . rand(100, 999);
// Begin transaction
mysqli_begin_transaction($conn);
try {
// Insert into transaksi table with cash_amount and change_amount
$stmt = mysqli_prepare($conn, "INSERT INTO transaksi (kode_transaksi, total_item, total, metode_pembayaran, cash_amount, change_amount) VALUES (?, ?, ?, 'Cash', ?, ?)");
if (!$stmt) {
throw new Exception("Failed to prepare transaction statement: " . mysqli_error($conn));
}
mysqli_stmt_bind_param($stmt, "siddd", $transactionCode, $totalItems, $total, $cashAmount, $changeAmount);
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
throw new Exception("Failed to execute transaction statement: " . mysqli_stmt_error($stmt));
}
// Get the ID of the transaction just created
$transactionId = mysqli_insert_id($conn);
if (!$transactionId) {
throw new Exception("Failed to get transaction ID");
}
// Insert each item into detail_transaksi
foreach ($items as $item) {
$product = getProductById($item['product_id']);
if (!$product) {
throw new Exception("Product not found: ID=" . $item['product_id']);
}
$itemTotal = $product['harga'] * $item['quantity'];
$stmt = mysqli_prepare($conn, "INSERT INTO detail_transaksi (id_transaksi, id_barangK, jumlah, harga_satuan, total_harga) VALUES (?, ?, ?, ?, ?)");
if (!$stmt) {
throw new Exception("Failed to prepare detail statement: " . mysqli_error($conn));
}
mysqli_stmt_bind_param($stmt, "iiddd", $transactionId, $item['product_id'], $item['quantity'], $product['harga'], $itemTotal);
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
throw new Exception("Failed to execute detail statement: " . mysqli_stmt_error($stmt));
}
// Update inventory (reduce stock)
$stmt = mysqli_prepare($conn, "UPDATE barang_kasir SET stok = stok - ? WHERE id_barangK = ? AND stok >= ?");
if (!$stmt) {
throw new Exception("Failed to prepare stock update statement: " . mysqli_error($conn));
}
mysqli_stmt_bind_param($stmt, "iii", $item['quantity'], $item['product_id'], $item['quantity']);
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
throw new Exception("Failed to execute stock update statement: " . mysqli_stmt_error($stmt));
}
// Check if stock was actually updated (prevents overselling)
if (mysqli_affected_rows($conn) == 0) {
throw new Exception("Insufficient stock for product: " . $product['nama_barang']);
}
}
// Commit transaction
mysqli_commit($conn);
return [
'success' => true,
'transaction_id' => $transactionId,
'transaction_code' => $transactionCode,
'cash_amount' => $cashAmount,
'change_amount' => $changeAmount
];
} catch (Exception $e) {
// Rollback in case of error
mysqli_rollback($conn);
error_log("Transaction failed: " . $e->getMessage());
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
/**
* Get transaction by ID
*
* @param int $transactionId The transaction ID
* @return array|null Transaction details or null if not found
*/
function getTransactionById($transactionId) {
global $conn;
try {
// Get transaction details
$stmt = mysqli_prepare($conn, "SELECT * FROM transaksi WHERE id_transaksi = ?");
if (!$stmt) {
error_log("Query preparation failed: " . mysqli_error($conn));
return null;
}
mysqli_stmt_bind_param($stmt, "i", $transactionId);
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
error_log("Query execution failed: " . mysqli_stmt_error($stmt));
mysqli_stmt_close($stmt);
return null;
}
$result = mysqli_stmt_get_result($stmt);
$transaction = mysqli_fetch_assoc($result);
if (!$transaction) {
return null;
}
// Get transaction items
$stmt = mysqli_prepare($conn, "
SELECT dt.*, b.nama_barang, b.kode_barang, j.nama_jenis
FROM detail_transaksi dt
JOIN barang_kasir b ON dt.id_barangK = b.id_barangK
JOIN jenis_barang j ON b.id_jenis = j.id_jenis
WHERE dt.id_transaksi = ?
");
if (!$stmt) {
error_log("Query preparation failed: " . mysqli_error($conn));
return $transaction; // Return transaction without items
}
mysqli_stmt_bind_param($stmt, "i", $transactionId);
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
error_log("Query execution failed: " . mysqli_stmt_error($stmt));
mysqli_stmt_close($stmt);
return $transaction; // Return transaction without items
}
$result = mysqli_stmt_get_result($stmt);
$items = [];
while ($row = mysqli_fetch_assoc($result)) {
$items[] = $row;
}
$transaction['items'] = $items;
return $transaction;
} catch (Exception $e) {
error_log("Exception in getTransactionById: " . $e->getMessage());
return null;
}
}
/**
* Get recent transactions
*
* @param int $limit Maximum number of transactions to return
* @return array List of recent transactions
*/
function getRecentTransactions($limit = 10) {
global $conn;
try {
$query = "SELECT * FROM transaksi ORDER BY created_at DESC LIMIT " . intval($limit);
$result = mysqli_query($conn, $query);
if (!$result) {
error_log("Query failed: " . mysqli_error($conn));
return [];
}
$transactions = [];
while ($row = mysqli_fetch_assoc($result)) {
$transactions[] = $row;
}
return $transactions;
} catch (Exception $e) {
error_log("Exception in getRecentTransactions: " . $e->getMessage());
return [];
}
}
/**
* Get current user information
*
* @return array|null User details or null if not found
*/
function getCurrentUser() {
global $conn;
try {
if (isset($_SESSION['user_id'])) {
$stmt = mysqli_prepare($conn, "SELECT id_kasir, username, role, phone FROM kasir WHERE id_kasir = ?");
if (!$stmt) {
error_log("Query preparation failed: " . mysqli_error($conn));
return null;
}
mysqli_stmt_bind_param($stmt, "i", $_SESSION['user_id']);
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
error_log("Query execution failed: " . mysqli_stmt_error($stmt));
mysqli_stmt_close($stmt);
return null;
}
$result = mysqli_stmt_get_result($stmt);
$user = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
return $user;
}
return null;
} catch (Exception $e) {
error_log("Exception in getCurrentUser: " . $e->getMessage());
return null;
}
}
/**
* Get product by barcode (kode_barang)
*
* @param string $barcode The product barcode
* @return array|null Product details or null if not found
*/
function getProductByBarcode($barcode) {
global $conn;
try {
$stmt = mysqli_prepare($conn, "SELECT b.*, j.nama_jenis FROM barang_kasir b
LEFT JOIN jenis_barang j ON b.id_jenis = j.id_jenis
WHERE b.kode_barang = ?");
if (!$stmt) {
error_log("Query preparation failed: " . mysqli_error($conn));
return null;
}
mysqli_stmt_bind_param($stmt, "s", $barcode);
$executed = mysqli_stmt_execute($stmt);
if (!$executed) {
error_log("Query execution failed: " . mysqli_stmt_error($stmt));
mysqli_stmt_close($stmt);
return null;
}
$result = mysqli_stmt_get_result($stmt);
$product = mysqli_fetch_assoc($result);
mysqli_stmt_close($stmt);
// Log untuk debugging
if ($product) {
error_log("Product found: ID=" . $product['id_barangK'] . ", Name=" . $product['nama_barang']);
} else {
error_log("Product not found for barcode: " . $barcode);
}
return $product;
} catch (Exception $e) {
error_log("Exception in getProductByBarcode: " . $e->getMessage());
return null;
}
}

View File

@ -0,0 +1,245 @@
<?php
// Pencegahan error tampil di output (sangat penting untuk AJAX)
ini_set('display_errors', 0);
error_reporting(E_ALL);
// Pastikan output hanya JSON, bukan error PHP
header('Content-Type: application/json');
// Include transaction functions
require_once 'configtrans.php';
// Fungsi untuk mengirim respons JSON dan keluar
function sendResponse($data) {
echo json_encode($data);
exit;
}
// Tangkap semua error untuk mencegah tampil di output
function handleErrors($errno, $errstr, $errfile, $errline) {
error_log("PHP Error ($errno): $errstr in $errfile on line $errline");
sendResponse([
'success' => false,
'message' => 'Terjadi kesalahan internal. Silakan coba lagi.',
'product' => null,
'cart_html' => '',
'cart_totals' => [
'items' => 0,
'total' => 0,
'formatted_total' => '0'
]
]);
return true;
}
set_error_handler('handleErrors');
// Try-catch untuk menangkap semua exception
try {
// Pastikan sesi dimulai
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Log request untuk debugging
error_log("Barcode scan request received: " . json_encode($_POST));
// Initialize response
$response = [
'success' => false,
'message' => 'Barcode tidak valid',
'product' => null,
'cart_html' => '',
'cart_totals' => [
'items' => 0,
'total' => 0,
'formatted_total' => '0'
]
];
// Check if barcode is provided
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['barcode'])) {
$barcode = trim($_POST['barcode']);
// Validate barcode
if (empty($barcode)) {
$response['message'] = "Barcode tidak boleh kosong";
sendResponse($response);
}
// Get product by barcode (kode_barang)
$product = getProductByBarcode($barcode);
if (!$product) {
$response['message'] = "Produk tidak ditemukan untuk barcode: " . htmlspecialchars($barcode);
sendResponse($response);
}
// Check stock availability
if ($product['stok'] <= 0) {
$response['message'] = "Produk " . htmlspecialchars($product['nama_barang']) . " stok habis";
sendResponse($response);
}
// Initialize cart if not exists
if (!isset($_SESSION['cart']) || !is_array($_SESSION['cart'])) {
$_SESSION['cart'] = [];
error_log("Cart initialized");
}
$productId = $product['id_barangK'];
$quantity = 1; // Default quantity is 1
// Check if product already in cart, update quantity if exists
$exists = false;
foreach ($_SESSION['cart'] as $key => $item) {
if ($item['id'] == $productId) {
// Only add if it won't exceed stock
if ($item['quantity'] < $product['stok']) {
$_SESSION['cart'][$key]['quantity'] += 1;
} else {
// Stock limit reached
$response['success'] = false;
$response['message'] = "Tidak dapat menambahkan unit lagi. Batas stok tercapai.";
sendResponse($response);
}
$exists = true;
break;
}
}
// If not exists, add to cart
if (!$exists) {
$_SESSION['cart'][] = [
'id' => $productId,
'name' => $product['nama_barang'],
'code' => $product['kode_barang'],
'price' => $product['harga'],
'quantity' => $quantity,
'max_stock' => $product['stok']
];
}
// Set success response
$response['success'] = true;
$response['message'] = 'Produk ditambahkan ke keranjang';
$response['product'] = [
'id' => $product['id_barangK'],
'name' => $product['nama_barang'],
'code' => $product['kode_barang'],
'price' => $product['harga'],
'stock' => $product['stok']
];
// Generate cart HTML
$response['cart_html'] = generateCartHTML();
// Calculate cart totals
$cartItems = count($_SESSION['cart']);
$total = 0;
foreach ($_SESSION['cart'] as $item) {
$total += $item['price'] * $item['quantity'];
}
$response['cart_totals'] = [
'items' => $cartItems,
'total' => $total,
'formatted_total' => number_format($total, 0, ',', '.')
];
// Log cart state for debugging
error_log("Cart updated. Items: " . $cartItems . ", Total: " . $total);
}
// Send the response
sendResponse($response);
} catch (Exception $e) {
// Log exception
error_log("Exception in get_product_by_barcode.php: " . $e->getMessage());
// Return error response
sendResponse([
'success' => false,
'message' => 'Terjadi kesalahan: ' . $e->getMessage(),
'product' => null,
'cart_html' => '',
'cart_totals' => [
'items' => 0,
'total' => 0,
'formatted_total' => '0'
]
]);
}
/**
* Generate HTML for cart items
*
* @return string HTML content for cart
*/
function generateCartHTML() {
if (isset($_SESSION['cart']) && !empty($_SESSION['cart'])) {
$html = '';
foreach ($_SESSION['cart'] as $index => $item) {
$html .= '
<ul class="product-lists">
<li>
<div class="productimg">
<div class="productimgs">
<img src="../../bootstrap/assets/img/product/product30.jpg" alt="img" />
</div>
<div class="productcontet">
<h4>' . htmlspecialchars($item['name']) . '</h4>
<div class="productlinkset">
<h5>' . htmlspecialchars($item['code']) . '</h5>
</div>
<div class="increment-decrement">
<div class="input-groups">
<form method="post" action="index.php" class="cart-item-form">
<input type="hidden" name="product_id" value="' . intval($item['id']) . '">
<input type="hidden" name="update_cart" value="1">
<div class="quantity-control-container">
<button type="button" class="btn btn-sm btn-light quantity-btn decrement">-</button>
<input type="text" name="quantity" value="' . intval($item['quantity']) . '"
class="quantity-field form-control mx-1"
style="width: 45px; text-align: center;"
data-max-stock="' . intval($item['max_stock']) . '" />
<button type="button" class="btn btn-sm btn-light quantity-btn increment">+</button>
<button type="submit" class="btn btn-sm btn-primary ms-1 update-cart-btn">
<i class="fa fa-check"></i>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</li>
<li>Rp. ' . number_format($item['price'] * $item['quantity'], 0, ',', '.') . '</li>
<li>
<a href="javascript:void(0);" class="delete-cart-item" data-index="' . $index . '">
<img src="../../bootstrap/assets/img/icons/delete-2.svg" alt="img" />
</a>
</li>
</ul>';
}
// Add script to enable functionalities
$html .= '
<script>
// Setup event handlers for the new cart items
if (typeof setupQuantityControls === "function") {
setupQuantityControls();
}
if (typeof setupCartDeletionConfirmation === "function") {
setupCartDeletionConfirmation();
}
// Dispatch custom event to notify that cart was updated
document.dispatchEvent(new CustomEvent("cartUpdated"));
</script>';
return $html;
} else {
return '<p class="text-center">Keranjang Anda kosong.</p>';
}
}

1147
views/transaction/index.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,482 @@
/**
* Ayula Store POS System
* Main JavaScript file for transaction page functionality
*/
$(document).ready(function() {
// Initialize globals
initializeGlobals();
// Setup event handlers
setupEventHandlers();
// Initialize UI components
initializeUI();
// Check and update the "Clear All" button state on page load
updateClearCartButtonInitialState();
});
/**
* Initialize global variables and state
*/
function initializeGlobals() {
// Variables to track selected products
window.selectedProducts = [];
window.multiSelectToolbar = $('.multi-select-toolbar');
// Store product stock information
window.productStock = {};
$('.productset').each(function() {
const productId = $(this).data('product-id');
const stockText = $(this).find('.productsetimg h6').text();
const stockValue = parseInt(stockText.replace('Stok: ', '')) || 0;
window.productStock[productId] = stockValue;
});
}
/**
* Check and update the "Clear All" button state on page load
*/
function updateClearCartButtonInitialState() {
const cartItemsCount = $('.product-lists').length;
console.log('Initial cart items count:', cartItemsCount);
// Get the clear cart button
const clearCartBtn = $('#clear-cart-btn');
if (cartItemsCount > 0) {
// Enable the clear cart button
clearCartBtn.attr('href', 'javascript:void(0);')
.removeClass('disabled')
.css({
'opacity': '1',
'cursor': 'pointer'
});
console.log('Clear cart button initially enabled');
} else {
// Disable the clear cart button
clearCartBtn.attr('href', '#')
.addClass('disabled')
.css({
'opacity': '0.5',
'cursor': 'not-allowed'
});
console.log('Clear cart button initially disabled');
}
}
/**
* Setup all event handlers for the page
*/
function setupEventHandlers() {
// Product card selection
setupProductSelection();
// Toolbar actions
setupToolbarActions();
}
/**
* Initialize UI components
*/
function initializeUI() {
// Hide all check marks initially
$('.check-product i').hide();
// Trigger toggle button click to activate it when page loads
setTimeout(function() {
$("#toggle_btn").trigger('click');
}, 100);
// Focus search field when search icon is clicked
$('.responsive-search').on('click', function() {
setTimeout(function() {
$('input[name="search"]').focus();
}, 100);
});
// Focus the page header search field on click
$('.product-search-form input[name="search"]').on('click', function() {
$(this).focus();
});
// Enable live search functionality
setupLiveSearch();
// Enable AJAX category navigation
setupCategoryNavigation();
// Initialize cash payment functionality
setupCashPayment();
}
/**
* Setup product selection functionality
*/
function setupProductSelection() {
// Click on product card to toggle selection
$('.productset').on('click', function(e) {
// Only handle clicks on the card itself, not buttons or links inside
if ($(e.target).closest('button, a, form').length === 0) {
const productId = $(this).data('product-id');
const checkbox = $(this).find('.product-checkbox');
const isChecked = checkbox.prop('checked');
console.log('Clicked product ID:', productId);
// Toggle checkbox
checkbox.prop('checked', !isChecked);
if (!isChecked) {
// Add to selected list and highlight
window.selectedProducts.push(productId);
$(this).addClass('selected');
$(this).find('.check-product i').show();
} else {
// Remove from selected list and unhighlight
window.selectedProducts = window.selectedProducts.filter(id => id !== productId);
$(this).removeClass('selected');
$(this).find('.check-product i').hide();
}
console.log('Selected products:', window.selectedProducts);
updateToolbar();
}
});
}
/**
* Setup toolbar action buttons
*/
function setupToolbarActions() {
// Cancel selection button
$('#cancel-selection').on('click', function() {
// Uncheck all checkboxes and hide check marks
$('.product-checkbox').prop('checked', false);
$('.productset').removeClass('selected');
$('.check-product i').hide();
window.selectedProducts = [];
updateToolbar();
});
// Add selected products to cart
$('#add-selected-to-cart').on('click', function() {
if (window.selectedProducts.length > 0) {
console.log('Adding products to cart:', window.selectedProducts);
// Create a form to submit selected products
const form = $('<form>', {
method: 'post',
action: 'index.php'
});
// Add each product ID as a hidden input
window.selectedProducts.forEach(function(productId) {
form.append(
$('<input>').attr({
type: 'hidden',
name: 'product_ids[]',
value: productId
})
);
});
// Log form contents before submission
console.log('Form contents:', form.serialize());
// Add the form to the body and submit it
$('body').append(form);
form.submit();
}
});
}
/**
* Update the toolbar state based on selected products
*/
function updateToolbar() {
if (window.selectedProducts.length > 0) {
$('.selected-count').text(window.selectedProducts.length + ' item' + (window.selectedProducts.length > 1 ? 's' : '') + ' selected');
window.multiSelectToolbar.addClass('active');
} else {
window.multiSelectToolbar.removeClass('active');
}
}
/**
* Setup live search functionality
*/
function setupLiveSearch() {
// Handle both search inputs - top nav and product header search
const searchInputs = $('input[name="search"]');
// Store the original URL for reference
const originalUrl = window.location.href.split('?')[0];
const urlParams = new URLSearchParams(window.location.search);
// Clear search buttons functionality
$('.product-search-form a, .search-addon a').on('click', function(e) {
e.preventDefault();
urlParams.delete('search');
// Build the new URL
let newUrl = originalUrl;
const paramString = urlParams.toString();
if (paramString) {
newUrl += '?' + paramString;
}
// Navigate to the new URL
window.location.href = newUrl;
});
// Custom form submission to prevent loader
$('.product-search-form form, .top-nav-search form').on('submit', function(e) {
e.preventDefault(); // Prevent the default form submission
// Get the search query
const searchQuery = $(this).find('input[name="search"]').val().trim();
// Get any type filter
const typeParam = $(this).find('input[name="type"]').val();
// Build the URL
let newUrl = originalUrl;
if (searchQuery || typeParam) {
newUrl += '?';
if (searchQuery) {
newUrl += 'search=' + encodeURIComponent(searchQuery);
if (typeParam) {
newUrl += '&';
}
}
if (typeParam) {
newUrl += 'type=' + encodeURIComponent(typeParam);
}
}
// Use AJAX to fetch the page content
$.ajax({
url: newUrl,
type: 'GET',
beforeSend: function() {
// Add a simple loading indicator to the product area
$('.tab_content').addClass('loading');
$('.tab_content > .row').css('opacity', '0.5');
},
success: function(response) {
// Extract and replace just the product content
const $response = $(response);
const newProductContent = $response.find('.tab_content').html();
// Update the DOM with new content
$('.tab_content').html(newProductContent);
// Update browser URL without reloading
history.pushState({}, '', newUrl);
// Update search info area if it exists
const searchInfoContent = $response.find('.search-results-info').html();
if (searchInfoContent) {
if ($('.search-results-info').length) {
$('.search-results-info').html(searchInfoContent);
} else {
$('<div class="search-results-info mb-3"></div>')
.html(searchInfoContent)
.insertAfter('.page-header');
}
$('.search-results-info').show();
} else {
$('.search-results-info').hide();
}
// Update the other search input with the same value
searchInputs.val(searchQuery);
// Reset the loading state
$('.tab_content').removeClass('loading');
$('.tab_content > .row').css('opacity', '1');
// Reinitialize product selection for newly loaded products
setupProductSelection();
},
error: function() {
// If something goes wrong, just do a normal page load
window.location.href = newUrl;
}
});
});
}
/**
* Setup AJAX-based category navigation
*/
function setupCategoryNavigation() {
// Original URL for reference
const originalUrl = window.location.href.split('?')[0];
// Add click event handlers to all category links
$('.tabs li a.category-tab').on('click', function(e) {
e.preventDefault();
// Get category type from URL
const href = $(this).attr('href');
const url = new URL(href, window.location.origin);
const typeParam = url.searchParams.get('type');
// Get current search query if any
const currentUrl = new URL(window.location.href);
const searchQuery = currentUrl.searchParams.get('search');
// Build the target URL
let targetUrl = originalUrl;
const params = new URLSearchParams();
// Add type parameter if set
if (typeParam) {
params.set('type', typeParam);
}
// Preserve search query if exists
if (searchQuery) {
params.set('search', searchQuery);
}
// Append parameters to URL if any
const paramString = params.toString();
if (paramString) {
targetUrl += '?' + paramString;
}
// Highlight the active category tab
$('.tabs li').removeClass('active');
$(this).closest('li').addClass('active');
// Use AJAX to fetch the category products
$.ajax({
url: targetUrl,
type: 'GET',
beforeSend: function() {
// Add loading indicator to product area
$('.tab_content').addClass('loading');
$('.tab_content > .row').css('opacity', '0.5');
},
success: function(response) {
// Extract and replace just the product content
const $response = $(response);
const newProductContent = $response.find('.tab_content').html();
// Update the DOM with new content
$('.tab_content').html(newProductContent);
// Update browser URL without reloading
history.pushState({}, '', targetUrl);
// Reset the loading state
$('.tab_content').removeClass('loading');
$('.tab_content > .row').css('opacity', '1');
// Reinitialize product selection for newly loaded products
setupProductSelection();
},
error: function() {
// If something goes wrong, just do a normal page load
window.location.href = targetUrl;
}
});
});
}
/**
* Setup cash payment functionality
*/
function setupCashPayment() {
const cashInput = $('#cash-amount');
const changeDisplay = $('#change-amount');
const changeContainer = $('#change-container');
const hiddenCashAmount = $('#hidden-cash-amount');
const hiddenChangeAmount = $('#hidden-change-amount');
const totalAmount = parseFloat($('.total-value h6').text().replace(/[^\d]/g, ''));
// Format cash input with thousand separators
cashInput.on('input', function() {
// Remove non-numeric characters
let value = $(this).val().replace(/[^\d]/g, '');
// Convert to number and format with thousand separator
if (value !== '') {
const numericValue = parseInt(value);
// Calculate change
const change = numericValue - totalAmount;
// Update hidden fields for form submission
hiddenCashAmount.val(numericValue);
hiddenChangeAmount.val(Math.max(0, change));
// Format display value
$(this).val(formatNumber(numericValue));
// Show change if payment is sufficient
if (change >= 0) {
changeDisplay.text('Rp. ' + formatNumber(change));
changeContainer.show();
} else {
changeContainer.hide();
}
} else {
$(this).val('');
changeContainer.hide();
}
});
// Quick cash buttons
$('.quick-cash').on('click', function() {
const value = $(this).data('value');
cashInput.val(formatNumber(value));
// Calculate change
const change = value - totalAmount;
// Update hidden fields for form submission
hiddenCashAmount.val(value);
hiddenChangeAmount.val(Math.max(0, change));
// Show change
if (change >= 0) {
changeDisplay.text('Rp. ' + formatNumber(change));
changeContainer.show();
} else {
changeContainer.hide();
}
});
// Form submission check
$('#checkout-form').on('submit', function(e) {
const cashValue = parseFloat(hiddenCashAmount.val());
// Check if cash amount is sufficient
if (cashValue < totalAmount) {
e.preventDefault();
alert('Cash amount is not sufficient!');
return false;
}
return true;
});
// Trigger input event to initialize on page load
cashInput.trigger('input');
}
/**
* Format number with thousand separator
*/
function formatNumber(number) {
return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}

View File

@ -0,0 +1,453 @@
<?php
// Memasukkan fungsi transaksi
require_once 'configtrans.php';
// Memulai sesi
// session_start();
$userRole = $_SESSION['role']; // 'user' or 'admin'
// Memeriksa apakah ID transaksi tersedia
if (!isset($_GET['id'])) {
// Arahkan ke halaman transaksi jika ID tidak ada
header('Location: index.php');
exit;
}
// Mendapatkan ID transaksi dari URL
$transactionId = $_GET['id'];
// Mendapatkan detail transaksi
$transaction = getTransactionById($transactionId);
// Memeriksa apakah transaksi ada
if (!$transaction) {
// Arahkan ke halaman transaksi jika transaksi tidak ditemukan
header('Location: index.php');
exit;
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=0"
/>
<meta name="description" content="POS - Template Admin Bootstrap" />
<meta
name="keywords"
content="admin, estimasi, bootstrap, bisnis, korporat, kreatif, faktur, html5, responsif, Proyek"
/>
<meta name="author" content="Dreamguys - Template Admin Bootstrap" />
<meta name="robots" content="noindex, nofollow" />
<title>Ayula Store POS - Transaksi Sukses</title>
<link
rel="shortcut icon"
type="image/x-icon"
href="../../bootstrap/assets/img/favicon.jpg"
/>
<link rel="stylesheet" href="../../bootstrap/assets/css/bootstrap.min.css" />
<link rel="stylesheet" href="../../bootstrap/assets/css/animate.css" />
<link rel="stylesheet" href="../../bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css" />
<link rel="stylesheet" href="../../bootstrap/assets/plugins/fontawesome/css/all.min.css" />
<link rel="stylesheet" href="../../bootstrap/assets/css/style.css" />
<style>
/* Gaya layar reguler */
@media screen {
.print-only {
display: none !important;
}
}
/* Gaya cetak */
@media print {
/* Sembunyikan semua kecuali struk */
body * {
visibility: hidden;
}
.print-receipt, .print-receipt * {
visibility: visible;
}
.print-receipt {
position: absolute;
left: 0;
top: 0;
width: 80mm; /* Lebar untuk struk thermal */
padding: 2mm;
margin: 0;
font-size: 10pt;
}
/* Sembunyikan elemen hanya untuk layar */
.screen-only {
display: none !important;
}
/* Tampilkan elemen hanya untuk cetak */
.print-only {
display: block !important;
}
/* Gaya struk */
.receipt-header {
text-align: center;
border-bottom: 1px dashed #000;
padding-bottom: 5px;
margin-bottom: 5px;
}
.receipt-info {
margin-bottom: 5px;
font-size: 9pt;
}
.receipt-table {
width: 100%;
font-size: 8pt;
border-collapse: collapse;
}
.receipt-table th, .receipt-table td {
padding: 2px 0;
}
.receipt-table th {
text-align: left;
border-bottom: 1px solid #000;
}
.receipt-footer {
text-align: center;
border-top: 1px dashed #000;
padding-top: 5px;
margin-top: 5px;
font-size: 8pt;
}
.receipt-total {
border-top: 1px solid #000;
margin-top: 5px;
padding-top: 5px;
}
.text-right {
text-align: right;
}
}
</style>
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"></div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img">
<img src="../../src/img/userprofile.png" alt="" />
<span class="status online"></span>
</span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img">
<img src="../../src/img/userprofile.png" alt="" />
<span class="status online"></span>
</span>
<div class="profilesets">
<h6><?php echo $isAdmin ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($username); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-ellipsis-v"></i>
</a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li class="active">
<a href="/ayula-store/views/dashboard/"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li>
<a href="/ayula-store/views/transaction/"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
POS</span></a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Produk</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php">Daftar Produk</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Produk</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/purchase1.svg" alt="img" /><span>
Pembelian</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="purchaselist.html">Daftar Pembelian</a></li>
<li><a href="addpurchase.html">Tambah Pembelian</a></li>
<li><a href="importpurchase.html">Impor Pembelian</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/time.svg" alt="img" /><span>
Laporan</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="inventoryreport.html">Laporan Inventaris</a></li>
<li><a href="/ayula-store/views/report/sales-report/">Laporan Penjualan</a></li>
<li><a href="purchasereport.html">Laporan Pembelian</a></li>
<li><a href="supplierreport.html">Laporan Pemasok</a></li>
</ul>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="row">
<div class="col-lg-8 col-sm-12 mx-auto">
<div class="card screen-only">
<div class="card-body">
<div class="text-center">
<h4 class="mt-2 mb-4"><i class="fa fa-check-circle text-success me-2"></i> Transaksi Berhasil</h4>
<p class="text-secondary">Transaksi Anda telah diproses dengan sukses</p>
<hr>
</div>
<div class="row mt-4">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label text-muted">ID Transaksi</label>
<div class="form-control-static"><?php echo $transaction['kode_transaksi']; ?></div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label text-muted">Tanggal</label>
<div class="form-control-static"><?php echo date('d M Y H:i', strtotime($transaction['tanggal'])); ?></div>
</div>
</div>
</div>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Barang</th>
<th>Jumlah</th>
<th class="text-end">Harga</th>
<th class="text-end">Total</th>
</tr>
</thead>
<tbody>
<?php foreach ($transaction['items'] as $item): ?>
<tr>
<td>
<div class="d-flex">
<div>
<h6><?php echo $item['nama_barang']; ?></h6>
<p class="text-muted mb-0"><?php echo $item['kode_barang']; ?></p>
</div>
</div>
</td>
<td><?php echo $item['jumlah']; ?></td>
<td class="text-end">Rp <?php echo number_format($item['harga_satuan'], 0, ',', '.'); ?></td>
<td class="text-end">Rp <?php echo number_format($item['total_harga'], 0, ',', '.'); ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="row justify-content-end mt-4">
<div class="col-lg-5">
<div class="card bg-light">
<div class="card-body">
<div class="d-flex justify-content-between border-top pt-2">
<h5>Total</h5>
<h5>Rp <?php echo number_format($transaction['total'], 0, ',', '.'); ?></h5>
</div>
<!-- Tambahkan informasi tunai dan kembalian -->
<?php if (isset($transaction['cash_amount']) && $transaction['cash_amount'] > 0): ?>
<div class="d-flex justify-content-between mt-3 mb-2">
<h6>Jumlah Tunai</h6>
<h6>Rp <?php echo number_format($transaction['cash_amount'], 0, ',', '.'); ?></h6>
</div>
<div class="d-flex justify-content-between">
<h6>Kembalian</h6>
<h6>Rp <?php echo number_format($transaction['change_amount'], 0, ',', '.'); ?></h6>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="text-center mt-4 screen-only">
<a href="index.php" class="btn btn-primary me-2">Transaksi Baru</a>
<button class="btn btn-secondary" onclick="window.print()">Cetak Struk</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Template Struk Thermal - Dioptimalkan untuk lebar 80mm -->
<div class="print-receipt print-only">
<div class="receipt-header">
<h3 style="margin:0;font-size:14pt;">AYULA STORE</h3>
<p style="margin:3px 0;font-size:9pt;">Senjayan, Kec. Gondang, Kabupaten Nganjuk, </p>
<p style="margin:3px 0;font-size:9pt;">Jawa Timur 64451</p>
<p style="margin:3px 0;font-size:9pt;">Telp: 0822 3472 2000</p>
<p style="margin:3px 0;font-size:8pt;">-------------------------------------</p>
</div>
<div class="receipt-info">
<table style="width:100%;font-size:9pt;">
<tr>
<td width="60%">No: <?php echo $transaction['kode_transaksi']; ?></td>
<td width="40%" style="text-align:right;">Kasir: Admin</td>
</tr>
<tr>
<td colspan="2">Tanggal: <?php echo date('d/m/Y H:i', strtotime($transaction['tanggal'])); ?></td>
</tr>
</table>
<p style="margin:2px 0;font-size:8pt;">-------------------------------------</p>
</div>
<table class="receipt-table" style="width:100%;font-size:8pt;">
<tr>
<th style="width:50%;text-align:left;">Barang</th>
<th style="width:10%;text-align:center;">Qty</th>
<th style="width:20%;text-align:right;">Harga</th>
<th style="width:20%;text-align:right;">Total</th>
</tr>
<?php foreach ($transaction['items'] as $item): ?>
<tr>
<td style="font-size:8pt;"><?php echo $item['nama_barang']; ?></td>
<td style="text-align:center;"><?php echo $item['jumlah']; ?></td>
<td style="text-align:right;"><?php echo number_format($item['harga_satuan'], 0, ',', '.'); ?></td>
<td style="text-align:right;"><?php echo number_format($item['total_harga'], 0, ',', '.'); ?></td>
</tr>
<?php endforeach; ?>
</table>
<p style="margin:5px 0 2px;font-size:8pt;">-------------------------------------</p>
<table style="width:100%;font-size:9pt;">
<tr style="font-weight:bold;">
<td style="text-align:right;">TOTAL:</td>
<td style="text-align:right;">Rp <?php echo number_format($transaction['total'], 0, ',', '.'); ?></td>
</tr>
<?php if (isset($transaction['cash_amount']) && $transaction['cash_amount'] > 0): ?>
<tr>
<td style="text-align:right;">Tunai:</td>
<td style="text-align:right;">Rp <?php echo number_format($transaction['cash_amount'], 0, ',', '.'); ?></td>
</tr>
<tr>
<td style="text-align:right;">Kembali:</td>
<td style="text-align:right;">Rp <?php echo number_format($transaction['change_amount'], 0, ',', '.'); ?></td>
</tr>
<?php endif; ?>
</table>
<div class="receipt-footer">
<p style="margin:5px 0 2px;font-size:8pt;">-------------------------------------</p>
<p style="margin:3px 0;font-size:9pt;">Terima Kasih Atas Kunjungan Anda</p>
<p style="margin:3px 0;font-size:8pt;">Barang yang sudah dibeli tidak dapat dikembalikan</p>
</div>
</div>
<script src="../../bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="../../bootstrap/assets/js/feather.min.js"></script>
<script src="../../bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="../../bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="../../bootstrap/assets/js/script.js"></script>
<script>
// Auto-print saat halaman ini dimuat - aktifkan jika perlu
// window.onload = function() {
// window.print();
// };
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

293
views/users/add-user.php Normal file
View File

@ -0,0 +1,293 @@
<?php
include('../../routes/db_conn.php');
session_start();
$userRole = $_SESSION['role'];
$username = $_SESSION['username']; // Mengambil username dari session
// Register new user data if the form is submitted
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Get the form data
$newUsername = $_POST['username'];
$password = $_POST['password'];
$confirmPassword = $_POST['confirm_password'];
$phone = $_POST['phone'];
$role = $_POST['role'];
// Validate form fields (optional but recommended)
if (!empty($newUsername) && !empty($password) && !empty($confirmPassword) && !empty($phone) && !empty($role)) {
// Check if phone number contains only numbers
if (!preg_match('/^[0-9]+$/', $phone)) {
$showUsernameModal = true;
$modalMessage = "Nomor telepon hanya boleh berisi angka.";
}
// Check if phone number length is valid (11-13 digits for Indonesian numbers)
else if (strlen($phone) < 11 || strlen($phone) > 13) {
$showUsernameModal = true;
$modalMessage = "Nomor telepon harus terdiri dari 11 hingga 13 angka.";
}
// Check if passwords match
else if ($password !== $confirmPassword) {
$showUsernameModal = true;
$modalMessage = "Password tidak sama.";
} else {
// Check if username already exists
$checkUsername = "SELECT username FROM kasir WHERE username = ?";
$checkStmt = $conn->prepare($checkUsername);
$checkStmt->bind_param("s", $newUsername);
$checkStmt->execute();
$result = $checkStmt->get_result();
if ($result->num_rows > 0) {
// Set flag to show modal
$showUsernameModal = true;
$modalMessage = "Username sudah digunakan. Silakan pilih username lain.";
} else {
// Hash the password
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Insert the user information into the database
$insertSql = "INSERT INTO kasir (username, password, phone, role) VALUES (?, ?, ?, ?)";
$insertStmt = $conn->prepare($insertSql);
$insertStmt->bind_param("ssss", $newUsername, $hashed_password, $phone, $role);
$insertStmt->execute();
// Redirect to the users list after the insert
header("Location: index.php");
exit;
}
}
} else {
// Show error if any form field is empty
$showUsernameModal = true;
$modalMessage = "Harap isi semua kolom.";
}
}
// Variable for modal display
$showUsernameModal = isset($showUsernameModal) ? $showUsernameModal : false;
$modalMessage = isset($modalMessage) ? $modalMessage : "";
$conn->close();
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="description" content="POS - Bootstrap Admin Template">
<meta name="keywords" content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects">
<meta name="author" content="Dreamguys - Bootstrap Admin Template">
<meta name="robots" content="noindex, nofollow">
<title>Ayula Store - Tambah Pengguna</title>
<link rel="shortcut icon" type="image/x-icon" href="../../src/img/smallest-ayula.png">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/style.css">
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"> </div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img">
<img src="../../src/img/userprofile.png" alt="" />
<span class="status online"></span>
</span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img">
<img src="../../src/img/userprofile.png" alt="" />
<span class="status online"></span>
</span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($username); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/reporttt/report.php"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Barang</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php" class="active">Daftar Barang</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Barang</a></li>
</ul>
</li>
<li >
<a href="/ayula-store/views/barang/topsis_restock_view.php"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
Analisa Barang</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php" class="active">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Manajemen Pengguna</h4>
<h6>Tambah Pengguna Baru</h6>
</div>
</div>
<div class="card">
<div class="card-body">
<form method="POST" action="add-user.php">
<div class="row">
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Nama</label>
<input type="text" name="username" class="form-control" required>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control" required>
</div>
<div class="form-group">
<label>Konfirmasi Password</label>
<input type="password" name="confirm_password" class="form-control" required>
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Telepon</label>
<input type="text" name="phone" class="form-control" required
oninput="this.value = this.value.replace(/[^0-9]/g, '');"
pattern="[0-9]{11,13}"
title="Masukkan nomor telepon yang valid (11-13 angka)"
minlength="11" maxlength="13">
<!-- <small class="form-text text-muted">Nomor telepon harus terdiri dari 11-13 angka.</small> -->
</div>
<div class="form-group">
<label>Peran</label>
<select name="role" class="form-control" required>
<option value="admin">Admin</option>
<option value="user">Karyawan</option>
</select>
</div>
</div>
<div class="col-lg-12">
<button type="submit" class="btn btn-submit me-2">Daftar</button>
<a href="/ayula-store/views/users/" class="btn btn-cancel">Batal</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="/ayula-store/bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/feather.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/script.js"></script>
<!-- Modal for notifications -->
<div class="modal fade" id="notificationModal" tabindex="-1" role="dialog" aria-labelledby="notificationModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="notificationModalLabel">Notifikasi</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center">
<i class="fas fa-exclamation-circle text-warning" style="font-size: 48px;"></i>
<p class="mt-3"><?php echo $modalMessage; ?></p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Mengerti</button>
</div>
</div>
</div>
</div>
<?php if ($showUsernameModal): ?>
<script>
// Show the modal when the page loads
document.addEventListener('DOMContentLoaded', function() {
var notificationModal = new bootstrap.Modal(document.getElementById('notificationModal'));
notificationModal.show();
});
</script>
<?php endif; ?>
</body>
</html>

View File

@ -0,0 +1,26 @@
<?php
include('../../routes/db_conn.php');
// Check if an ID is passed
if (isset($_GET['id'])) {
$id_kasir = $_GET['id'];
// Prepare SQL query to delete the user
$sql = "DELETE FROM kasir WHERE id_kasir = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id_kasir);
// Execute the query
if ($stmt->execute()) {
// Redirect back to the user list after successful deletion
header("Location: index.php");
exit;
} else {
// If deletion fails, show an error message
echo "Error deleting record: " . $conn->error;
}
}
// Close the connection
$conn->close();
?>

264
views/users/edit-user.php Normal file
View File

@ -0,0 +1,264 @@
<?php
session_start();
include('../../routes/db_conn.php');
$userRole = $_SESSION['role']; // 'user' or 'admin'
$username = $_SESSION['username']; // Menambahkan username dari session
// Check if the user ID is passed in the URL
if (isset($_GET['id'])) {
$id_kasir = $_GET['id'];
// Fetch user data from the database
$sql = "SELECT id_kasir, username, phone, role FROM kasir WHERE id_kasir = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $id_kasir); // Bind the id to the query
$stmt->execute();
$result = $stmt->get_result();
// Check if the user exists
if ($result->num_rows > 0) {
$user = $result->fetch_assoc();
} else {
echo "Pengguna tidak ditemukan.";
exit;
}
}
// Update user data if the form is submitted
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// Get the form data
$editUsername = $_POST['username'];
$password = $_POST['password'];
$phone = $_POST['phone'];
$role = $_POST['role'];
// Validate form fields (optional but recommended)
if (!empty($editUsername) && !empty($phone) && !empty($role)) {
// Check if password is provided
if (!empty($password)) {
// Hash the password
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Update with new password
$updateSql = "UPDATE kasir SET username = ?, password = ?, phone = ?, role = ? WHERE id_kasir = ?";
$updateStmt = $conn->prepare($updateSql);
$updateStmt->bind_param("ssssi", $editUsername, $hashed_password, $phone, $role, $id_kasir);
} else {
// Update without changing password
$updateSql = "UPDATE kasir SET username = ?, phone = ?, role = ? WHERE id_kasir = ?";
$updateStmt = $conn->prepare($updateSql);
$updateStmt->bind_param("sssi", $editUsername, $phone, $role, $id_kasir);
}
$updateStmt->execute();
// Redirect to the users list after the update
header("Location: index.php");
exit;
} else {
// Show error if any form field is empty
echo "<script>alert('Harap isi semua kolom yang diperlukan.');</script>";
}
}
$conn->close();
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0">
<meta name="description" content="POS - Bootstrap Admin Template">
<meta name="keywords" content="admin, estimates, bootstrap, business, corporate, creative, invoice, html5, responsive, Projects">
<meta name="author" content="Dreamguys - Bootstrap Admin Template">
<meta name="robots" content="noindex, nofollow">
<title>Ayula Store - Ubah Pengguna</title>
<link rel="shortcut icon" type="image/x-icon" href="../../src/img/smallest-ayula.png">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/style.css">
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"> </div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img">
<img src="../../src/img/userprofile.png" alt="" />
<span class="status online"></span>
</span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img">
<img src="../../src/img/userprofile.png" alt="" />
<span class="status online"></span>
</span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($username); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-ellipsis-v"></i>
</a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/reporttt/report.php"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Barang</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php" class="active">Daftar Barang</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Barang</a></li>
</ul>
</li>
<li >
<a href="/ayula-store/views/barang/topsis_restock_view.php"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
Analisa Barang</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<?php if ($userRole == 'admin') { ?>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<?php } ?>
<li><a href="/ayula-store/views/users/" class="active">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Manajemen Pengguna</h4>
<h6>Edit/Perbarui Pengguna</h6>
</div>
</div>
<div class="card">
<div class="card-body">
<form method="POST" action="edit-user.php?id=<?php echo $id_kasir; ?>">
<div class="row">
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Nama Pengguna</label>
<input type="text" name="username" class="form-control" value="<?php echo htmlspecialchars($user['username']); ?>" required>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control" placeholder="Kosongkan jika tidak ingin mengubah">
</div>
</div>
<div class="col-lg-3 col-sm-6 col-12">
<div class="form-group">
<label>Telepon</label>
<input type="text" name="phone" class="form-control" value="<?php echo htmlspecialchars($user['phone']); ?>" required>
</div>
<div class="form-group">
<label>Peran</label>
<select name="role" class="form-control" required>
<option value="admin" <?php echo $user['role'] == 'admin' ? 'selected' : ''; ?>>Admin</option>
<option value="user" <?php echo $user['role'] == 'user' ? 'selected' : ''; ?>>Karyawan</option>
</select>
</div>
</div>
<div class="col-lg-12">
<button type="submit" class="btn btn-submit me-2">Simpan</button>
<a href="index.php" class="btn btn-cancel">Batal</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="/ayula-store/bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/feather.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/script.js"></script>
</body>
</html>

296
views/users/index.php Normal file
View File

@ -0,0 +1,296 @@
<?php
include('../../routes/db_conn.php');
// Start the session to access the logged-in user information
session_start();
// // Ambil informasi user yang sedang login
$userRole = $_SESSION['role']; // 'user' atau 'admin' $username = $_SESSION['username']; // Menambahkan username dari session
// Query database untuk daftar kasir
$sql = "SELECT id_kasir, username, phone, role FROM kasir"; // Menghapus 'password' dari query
$result = $conn->query($sql);
if ($result->num_rows > 0) {
$kasirData = [];
while ($row = $result->fetch_assoc()) {
$kasirData[] = $row;
}
} else {
$kasirData = null; // Jika tidak ada data
}
// Menutup koneksi
$conn->close();
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Ayula Store - Pengguna</title>
<link rel="shortcut icon" type="image/x-icon" href="../../src/img/smallest-ayula.png">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/animate.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/select2/css/select2.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/dataTables.bootstrap4.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/fontawesome.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/plugins/fontawesome/css/all.min.css">
<link rel="stylesheet" href="/ayula-store/bootstrap/assets/css/style.css">
</head>
<body>
<div id="global-loader">
<div class="whirly-loader"> </div>
</div>
<div class="main-wrapper">
<div class="header">
<div class="header-left active">
<a href="/ayula-store/views/dashboard/" class="logo">
<img src="../../src/img/logoayula.png" alt="" />
</a>
<a href="/ayula-store/views/dashboard/" class="logo-small">
<img src="../../src/img/smallest-ayula.png" alt="" />
</a>
<a id="toggle_btn" href="javascript:void(0);"> </a>
</div>
<a id="mobile_btn" class="mobile_btn" href="#sidebar">
<span class="bar-icon">
<span></span>
<span></span>
<span></span>
</span>
</a>
<ul class="nav user-menu">
<li class="nav-item dropdown has-arrow main-drop">
<a href="javascript:void(0);" class="dropdown-toggle nav-link userset" data-bs-toggle="dropdown">
<span class="user-img">
<img src="../../src/img/userprofile.png" alt="" />
<span class="status online"></span>
</span>
</a>
<div class="dropdown-menu menu-drop-user">
<div class="profilename">
<div class="profileset">
<span class="user-img">
<img src="../../src/img/userprofile.png" alt="" />
<span class="status online"></span>
</span>
<div class="profilesets">
<h6><?php echo $userRole == 'admin' ? 'Admin' : 'Karyawan'; ?></h6>
<h5><?php echo htmlspecialchars($username); ?></h5>
</div>
</div>
<hr class="m-0" />
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<img src="../../src/img/warning.png" class="me-2" alt="img" /> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</li>
</ul>
<div class="dropdown mobile-user-menu">
<a href="javascript:void(0);" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-ellipsis-v"></i>
</a>
<div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/ayula-store/views/report-issue/">
<i class="fa fa-cog me-2"></i> Laporkan Masalah
</a>
<hr class="m-0" />
<a class="dropdown-item logout pb-0" href="../../views/logout.php"><img
src="../../bootstrap/assets/img/icons/log-out.svg"
class="me-2"
alt="img" />Keluar</a>
</div>
</div>
</div>
<div class="sidebar" id="sidebar">
<div class="sidebar-inner slimscroll">
<div id="sidebar-menu" class="sidebar-menu">
<ul>
<li>
<a href="/ayula-store/views/reporttt/report.php"><img src="../../bootstrap/assets/img/icons/dashboard.svg" alt="img" /><span>
Dashboard</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/product.svg" alt="img" /><span>
Barang</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/barang/productlist.php" class="active">Daftar Barang</a></li>
<li><a href="/ayula-store/views/barang/addproduct.php">Tambah Barang</a></li>
</ul>
</li>
<li >
<a href="/ayula-store/views/barang/topsis_restock_view.php"><img src="../../bootstrap/assets/img/icons/sales1.svg" alt="img" /><span>
Analisa Barang</span>
</a>
</li>
<li class="submenu">
<a href="javascript:void(0);"><img src="../../bootstrap/assets/img/icons/users1.svg" alt="img" /><span>
Pengguna</span>
<span class="menu-arrow"></span></a>
<ul>
<li><a href="/ayula-store/views/users/add-user.php">Pengguna Baru</a></li>
<li><a href="/ayula-store/views/users/" class="active">Daftar Pengguna</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<div class="page-wrapper">
<div class="content">
<div class="page-header">
<div class="page-title">
<h4>Daftar Pengguna</h4>
<h6>Kelola Pengguna Anda</h6>
</div>
<div class="page-btn">
<a href="add-user.php" class="btn btn-added"><img src="/ayula-store/bootstrap/assets/img/icons/plus.svg" alt="img">Tambah Pengguna</a>
</div>
</div>
<div class="card">
<div class="card-body">
<div class="table-responsive">
<table class="table datanew">
<thead>
<tr>
<th>Nama</th>
<th>Telepon</th>
<th>Peran</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
<?php
if ($kasirData) {
foreach ($kasirData as $kasir) {
echo "<tr>";
echo "<td>" . $kasir['username'] . "</td>";
echo "<td>" . $kasir['phone'] . "</td>";
echo "<td>" . ($kasir['role'] == 'admin' ? 'Admin' : 'Karyawan') . "</td>";
echo "<td>
<a class='me-3' href='edit-user.php?id=" . $kasir['id_kasir'] . "'>
<img src='/ayula-store/bootstrap/assets/img/icons/edit.svg' alt='img'>
</a>
<a class='me-3 delete-btn' href='#' data-id='" . $kasir['id_kasir'] . "'>
<img src='/ayula-store/bootstrap/assets/img/icons/delete.svg' alt='img'>
</a>
</td>";
}
echo "</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Modal for Delete Confirmation -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">Hapus Pengguna</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Apakah Anda yakin ingin menghapus pengguna ini?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-cancel" data-bs-dismiss="modal">Batal</button>
<a id="confirmDelete" href="#" class="btn btn-submit">Hapus</a>
</div>
</div>
</div>
</div>
</div>
<script src="/ayula-store/bootstrap/assets/js/jquery-3.6.0.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/feather.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.slimscroll.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/jquery.dataTables.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/dataTables.bootstrap4.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap.bundle.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/select2/js/select2.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/moment.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/bootstrap-datetimepicker.min.js"></script>
<script src="/ayula-store/bootstrap/assets/plugins/sweetalert/sweetalert2.all.min.js"></script>
<script src="/ayula-store/bootstrap/assets/js/script.js"></script>
<script>
$(document).ready(function() {
// Check if the table is already initialized before initializing it
if (!$.fn.dataTable.isDataTable('.datanew')) {
$('.datanew').DataTable({
language: {
search: "Cari:",
lengthMenu: "Tampilkan _MENU_ data",
info: "Menampilkan _START_ sampai _END_ dari _TOTAL_ data",
infoEmpty: "Menampilkan 0 sampai 0 dari 0 data",
infoFiltered: "(disaring dari _MAX_ total data)",
zeroRecords: "Tidak ada data yang cocok",
paginate: {
first: "Pertama",
last: "Terakhir",
next: "Selanjutnya",
previous: "Sebelumnya"
}
}
});
}
// Handling the delete button click
$('.delete-btn').on('click', function() {
var userId = $(this).data('id');
var deleteUrl = 'delete-user.php?id=' + userId;
// Set the delete link in the modal
$('#confirmDelete').attr('href', deleteUrl);
// Show the modal
$('#deleteModal').modal('show');
});
});
</script>
<script>
// Disable console logs and warnings
if (window.location.hostname === 'localhost') {
console.log = function() {}; // Disable console logs
console.warn = function() {}; // Disable console warnings
console.error = function() {}; // Disable console errors
window.alert = function() {}; // Disable alert popups
}
</script>
</body>
</html>