dua
This commit is contained in:
parent
8d0038be8c
commit
2be5f009ee
33
api/add_office.php
Normal file
33
api/add_office.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
// api/add_office.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($_POST['nama_kantor']) || empty($_POST['tipe_kantor'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Nama kantor and tipe are required.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nama_kantor = $_POST['nama_kantor'];
|
||||||
|
$alamat = $_POST['alamat'] ?? null;
|
||||||
|
$tipe_kantor = $_POST['tipe_kantor'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$sql = "INSERT INTO kantor (nama_kantor, alamat, tipe_kantor) VALUES (?, ?, ?)";
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute([$nama_kantor, $alamat, $tipe_kantor]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Office added successfully.']);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
48
api/add_user.php
Normal file
48
api/add_user.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
// api/add_user.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($_POST['nama_lengkap']) || empty($_POST['email']) || empty($_POST['password']) || empty($_POST['role'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Please fill all required fields.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nama_lengkap = $_POST['nama_lengkap'];
|
||||||
|
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
|
||||||
|
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||||
|
$role = $_POST['role'];
|
||||||
|
$id_kantor = !empty($_POST['id_kantor']) ? filter_input(INPUT_POST, 'id_kantor', FILTER_SANITIZE_NUMBER_INT) : null;
|
||||||
|
|
||||||
|
if (!$email) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid email format.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for existing email
|
||||||
|
try {
|
||||||
|
$stmt = db()->prepare("SELECT id FROM users WHERE email = ?");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Email already exists.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "INSERT INTO users (nama_lengkap, email, password, role, id_kantor) VALUES (?, ?, ?, ?, ?)";
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute([$nama_lengkap, $email, $password, $role, $id_kantor]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => 'User added successfully.']);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
42
api/delete_asset.php
Normal file
42
api/delete_asset.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Basic security checks
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Akses ditolak.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Metode tidak diizinkan.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$assetId = $data['id'] ?? null;
|
||||||
|
|
||||||
|
if (!$assetId) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'ID Aset tidak valid.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM aset WHERE id = ?");
|
||||||
|
$stmt->execute([$assetId]);
|
||||||
|
|
||||||
|
if ($stmt->rowCount() > 0) {
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Aset berhasil dihapus.']);
|
||||||
|
} else {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Aset tidak ditemukan.']);
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
// In production, log this error instead of echoing it.
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Gagal menghapus aset: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
46
api/delete_office.php
Normal file
46
api/delete_office.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
// api/delete_office.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($_POST['id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Office ID is required.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// The foreign key constraints in `users` and `aset` tables are set to
|
||||||
|
// `ON DELETE SET NULL` and `ON DELETE RESTRICT` respectively.
|
||||||
|
// We should check for assets before deleting.
|
||||||
|
|
||||||
|
$stmt_check = db()->prepare("SELECT COUNT(*) FROM aset WHERE id_kantor_lokasi = ?");
|
||||||
|
$stmt_check->execute([$id]);
|
||||||
|
if ($stmt_check->fetchColumn() > 0) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Cannot delete office. It is still associated with existing assets. Please reassign assets first.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no assets are linked, proceed with deletion.
|
||||||
|
// Users linked to this office will have their id_kantor set to NULL.
|
||||||
|
$stmt_delete = db()->prepare("DELETE FROM kantor WHERE id = ?");
|
||||||
|
$stmt_delete->execute([$id]);
|
||||||
|
|
||||||
|
if ($stmt_delete->rowCount() > 0) {
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Office deleted successfully.']);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Office not found or could not be deleted.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
42
api/delete_user.php
Normal file
42
api/delete_user.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
// api/delete_user.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($_POST['id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'User ID is required.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
|
||||||
|
// Prevent super admin from deleting themselves
|
||||||
|
if ($id == $_SESSION['user_id']) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'You cannot delete your own account.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Optional: Check if user has related records (e.g., assets) before deleting
|
||||||
|
// For now, we rely on the database foreign key constraint (ON DELETE SET NULL)
|
||||||
|
|
||||||
|
$stmt = db()->prepare("DELETE FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
|
||||||
|
if ($stmt->rowCount() > 0) {
|
||||||
|
echo json_encode(['success' => true, 'message' => 'User deleted successfully.']);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'User not found or could not be deleted.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
35
api/get_asset.php
Normal file
35
api/get_asset.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['error' => 'Akses ditolak.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$assetId = $_GET['id'] ?? null;
|
||||||
|
|
||||||
|
if (!$assetId) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['error' => 'ID Aset tidak valid.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM aset WHERE id = ?");
|
||||||
|
$stmt->execute([$assetId]);
|
||||||
|
$asset = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($asset) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode($asset);
|
||||||
|
} else {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['error' => 'Aset tidak ditemukan.']);
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => 'Gagal mengambil data aset: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
32
api/get_offices.php
Normal file
32
api/get_offices.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
// api/get_offices.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$sql = "SELECT * FROM kantor ORDER BY nama_kantor ASC";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
// If an ID is provided, fetch a single office
|
||||||
|
if (isset($_GET['id']) && !empty($_GET['id'])) {
|
||||||
|
$sql = "SELECT * FROM kantor WHERE id = ?";
|
||||||
|
$params[] = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$offices = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'offices' => $offices]);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
34
api/get_user.php
Normal file
34
api/get_user.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
// api/get_user.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_GET['id'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'User ID is required']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = db()->prepare("SELECT id, nama_lengkap, email, role, id_kantor FROM users WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
echo json_encode(['success' => true, 'user' => $user]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'User not found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
26
api/get_users.php
Normal file
26
api/get_users.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
// api/get_users.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$sql = "SELECT u.id, u.nama_lengkap, u.email, u.role, u.created_at, k.nama_kantor
|
||||||
|
FROM users u
|
||||||
|
LEFT JOIN kantor k ON u.id_kantor = k.id
|
||||||
|
ORDER BY u.nama_lengkap ASC";
|
||||||
|
$stmt = db()->query($sql);
|
||||||
|
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'users' => $users]);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
73
api/update_asset.php
Normal file
73
api/update_asset.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
http_response_code(405);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Metode tidak diizinkan.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
http_response_code(403);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Akses ditolak.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get data from POST request
|
||||||
|
$id = $_POST['id'] ?? null;
|
||||||
|
$nama_aset = $_POST['nama_aset'] ?? '';
|
||||||
|
$id_kategori = $_POST['id_kategori'] ?? null;
|
||||||
|
$id_kantor_lokasi = $_POST['id_kantor_lokasi'] ?? null;
|
||||||
|
$spesifikasi = $_POST['spesifikasi'] ?? '';
|
||||||
|
$tanggal_pembelian = $_POST['tanggal_pembelian'] ?? null;
|
||||||
|
$harga_pembelian = $_POST['harga_pembelian'] ?? null;
|
||||||
|
$vendor = $_POST['vendor'] ?? '';
|
||||||
|
$status = $_POST['status'] ?? '';
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($id) || empty($nama_aset) || empty($id_kategori) || empty($id_kantor_lokasi) || empty($tanggal_pembelian) || empty($harga_pembelian) || empty($status)) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Semua field yang wajib diisi harus diisi.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = "UPDATE aset SET
|
||||||
|
nama_aset = :nama_aset,
|
||||||
|
id_kategori = :id_kategori,
|
||||||
|
id_kantor_lokasi = :id_kantor_lokasi,
|
||||||
|
spesifikasi = :spesifikasi,
|
||||||
|
tanggal_pembelian = :tanggal_pembelian,
|
||||||
|
harga_pembelian = :harga_pembelian,
|
||||||
|
vendor = :vendor,
|
||||||
|
status = :status,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = :id";
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
|
||||||
|
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':nama_aset', $nama_aset, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':id_kategori', $id_kategori, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':id_kantor_lokasi', $id_kantor_lokasi, PDO::PARAM_INT);
|
||||||
|
$stmt->bindParam(':spesifikasi', $spesifikasi, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':tanggal_pembelian', $tanggal_pembelian, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':harga_pembelian', $harga_pembelian);
|
||||||
|
$stmt->bindParam(':vendor', $vendor, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':status', $status, PDO::PARAM_STR);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Aset berhasil diperbarui.']);
|
||||||
|
} else {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Gagal memperbarui aset.']);
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
// In production, log this error.
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
34
api/update_office.php
Normal file
34
api/update_office.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
// api/update_office.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($_POST['id']) || empty($_POST['nama_kantor']) || empty($_POST['tipe_kantor'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Incomplete data for update.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
$nama_kantor = $_POST['nama_kantor'];
|
||||||
|
$alamat = $_POST['alamat'] ?? null;
|
||||||
|
$tipe_kantor = $_POST['tipe_kantor'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$sql = "UPDATE kantor SET nama_kantor = ?, alamat = ?, tipe_kantor = ? WHERE id = ?";
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute([$nama_kantor, $alamat, $tipe_kantor, $id]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => 'Office updated successfully.']);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
56
api/update_user.php
Normal file
56
api/update_user.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
// api/update_user.php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once '../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Authentication and Authorization check
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($_POST['id']) || empty($_POST['nama_lengkap']) || empty($_POST['email']) || empty($_POST['role'])) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Incomplete data for update.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT);
|
||||||
|
$nama_lengkap = $_POST['nama_lengkap'];
|
||||||
|
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
|
||||||
|
$role = $_POST['role'];
|
||||||
|
$id_kantor = !empty($_POST['id_kantor']) ? filter_input(INPUT_POST, 'id_kantor', FILTER_SANITIZE_NUMBER_INT) : null;
|
||||||
|
|
||||||
|
if (!$email) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid email format.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if email is used by another user
|
||||||
|
$stmt = db()->prepare("SELECT id FROM users WHERE email = ? AND id != ?");
|
||||||
|
$stmt->execute([$email, $id]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Email is already in use by another account.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle password update
|
||||||
|
if (!empty($_POST['password'])) {
|
||||||
|
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||||
|
$sql = "UPDATE users SET nama_lengkap = ?, email = ?, password = ?, role = ?, id_kantor = ? WHERE id = ?";
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute([$nama_lengkap, $email, $password, $role, $id_kantor, $id]);
|
||||||
|
} else {
|
||||||
|
$sql = "UPDATE users SET nama_lengkap = ?, email = ?, role = ?, id_kantor = ? WHERE id = ?";
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute([$nama_lengkap, $email, $role, $id_kantor, $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['success' => true, 'message' => 'User updated successfully.']);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
@ -1,13 +1,15 @@
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
|
||||||
// --- Constants ---
|
// --- Constants ---
|
||||||
const API_BASE = 'api/';
|
const API_BASE = 'api/';
|
||||||
const addAssetForm = document.getElementById('addAssetForm');
|
const addAssetForm = document.getElementById('addAssetForm');
|
||||||
|
const editAssetForm = document.getElementById('editAssetForm');
|
||||||
const assetsTableBody = document.getElementById('assets-table-body');
|
const assetsTableBody = document.getElementById('assets-table-body');
|
||||||
const loadingIndicator = document.getElementById('loading-indicator');
|
const loadingIndicator = document.getElementById('loading-indicator');
|
||||||
const notificationToastEl = document.getElementById('notificationToast');
|
const notificationToastEl = document.getElementById('notificationToast');
|
||||||
const notificationToast = new bootstrap.Toast(notificationToastEl);
|
const notificationToast = new bootstrap.Toast(notificationToastEl);
|
||||||
|
const editAssetModalEl = document.getElementById('editAssetModal');
|
||||||
|
const editAssetModal = new bootstrap.Modal(editAssetModalEl);
|
||||||
|
|
||||||
// --- Functions ---
|
// --- Functions ---
|
||||||
|
|
||||||
@ -20,8 +22,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
function showToast(title, body, type = 'success') {
|
function showToast(title, body, type = 'success') {
|
||||||
const toastHeader = notificationToastEl.querySelector('.toast-header');
|
const toastHeader = notificationToastEl.querySelector('.toast-header');
|
||||||
|
|
||||||
// Reset classes
|
toastHeader.classList.remove('bg-success', 'text-white', 'bg-danger', 'text-white');
|
||||||
toastHeader.classList.remove('bg-success', 'text-white', 'bg-danger');
|
|
||||||
|
|
||||||
if (type === 'success') {
|
if (type === 'success') {
|
||||||
toastHeader.classList.add('bg-success', 'text-white');
|
toastHeader.classList.add('bg-success', 'text-white');
|
||||||
@ -37,40 +38,45 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
/**
|
/**
|
||||||
* Fetches data from the API.
|
* Fetches data from the API.
|
||||||
* @param {string} endpoint - The API endpoint to fetch from.
|
* @param {string} endpoint - The API endpoint to fetch from.
|
||||||
|
* @param {object} options - Optional fetch options.
|
||||||
* @returns {Promise<any>} - The JSON response.
|
* @returns {Promise<any>} - The JSON response.
|
||||||
*/
|
*/
|
||||||
async function fetchApi(endpoint) {
|
async function fetchApi(endpoint, options = {}) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(API_BASE + endpoint);
|
const response = await fetch(API_BASE + endpoint, options);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
const errorData = await response.json().catch(() => ({ message: 'Gagal memuat detail error.' }));
|
||||||
|
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
return await response.json();
|
return await response.json();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error fetching ${endpoint}:`, error);
|
console.error(`Error fetching ${endpoint}:`, error);
|
||||||
showToast('Error', `Gagal memuat data dari server: ${error.message}`, 'error');
|
showToast('Error', `Terjadi kesalahan: ${error.message}`, 'error');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates select dropdowns.
|
* Populates select dropdowns for both add and edit modals.
|
||||||
*/
|
*/
|
||||||
async function populateSelectOptions() {
|
async function populateSelectOptions() {
|
||||||
const data = await fetchApi('get_options.php');
|
const data = await fetchApi('get_options.php');
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
const kategoriSelect = document.getElementById('id_kategori');
|
const selects = [
|
||||||
const kantorSelect = document.getElementById('id_kantor_lokasi');
|
{ el: document.getElementById('id_kategori'), data: data.kategori, placeholder: 'Pilih Kategori...', name: 'nama_kategori' },
|
||||||
|
{ el: document.getElementById('id_kantor_lokasi'), data: data.kantor, placeholder: 'Pilih Lokasi...', name: 'nama_kantor' },
|
||||||
|
{ el: document.getElementById('edit_id_kategori'), data: data.kategori, placeholder: 'Pilih Kategori...', name: 'nama_kategori' },
|
||||||
|
{ el: document.getElementById('edit_id_kantor_lokasi'), data: data.kantor, placeholder: 'Pilih Lokasi...', name: 'nama_kantor' }
|
||||||
|
];
|
||||||
|
|
||||||
kategoriSelect.innerHTML = '<option value="">Pilih Kategori...</option>';
|
selects.forEach(s => {
|
||||||
data.kategori.forEach(item => {
|
if (s.el) {
|
||||||
kategoriSelect.innerHTML += `<option value="${item.id}">${item.nama_kategori}</option>`;
|
s.el.innerHTML = `<option value="">${s.placeholder}</option>`;
|
||||||
|
s.data.forEach(item => {
|
||||||
|
s.el.innerHTML += `<option value="${item.id}">${item[s.name]}</option>`;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
kantorSelect.innerHTML = '<option value="">Pilih Lokasi...</option>';
|
|
||||||
data.kantor.forEach(item => {
|
|
||||||
kantorSelect.innerHTML += `<option value="${item.id}">${item.nama_kantor}</option>`;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,10 +99,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
<td>${asset.nama_aset}</td>
|
<td>${asset.nama_aset}</td>
|
||||||
<td>${asset.nama_kategori}</td>
|
<td>${asset.nama_kategori}</td>
|
||||||
<td>${asset.nama_kantor}</td>
|
<td>${asset.nama_kantor}</td>
|
||||||
<td><span class="status-badge status-${asset.status}">${asset.status}</span></td>
|
<td><span class="status-badge status-${asset.status.toLowerCase()}">${asset.status}</span></td>
|
||||||
<td>
|
<td>
|
||||||
<button class="btn btn-sm btn-outline-primary" onclick="alert('Fitur Edit belum tersedia.')"><i data-feather="edit-2" class="feather-sm"></i></button>
|
<button class="btn btn-sm btn-outline-primary btn-edit" data-asset-id="${asset.id}" title="Edit">
|
||||||
<button class="btn btn-sm btn-outline-danger" onclick="alert('Fitur Hapus belum tersedia.')"><i data-feather="trash-2" class="feather-sm"></i></button>
|
<i data-feather="edit-2" class="feather-sm"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-outline-danger btn-delete" data-asset-id="${asset.id}" title="Hapus">
|
||||||
|
<i data-feather="trash-2" class="feather-sm"></i>
|
||||||
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
`;
|
`;
|
||||||
@ -107,12 +117,11 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
} else {
|
} else {
|
||||||
assetsTableBody.innerHTML = '<tr><td colspan="6" class="text-center text-danger">Gagal memuat data aset.</td></tr>';
|
assetsTableBody.innerHTML = '<tr><td colspan="6" class="text-center text-danger">Gagal memuat data aset.</td></tr>';
|
||||||
}
|
}
|
||||||
feather.replace(); // Re-initialize Feather icons
|
feather.replace();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the submission of the 'Add Asset' form.
|
* Handles the submission of the 'Add Asset' form.
|
||||||
* @param {Event} event - The form submission event.
|
|
||||||
*/
|
*/
|
||||||
async function handleAddAssetSubmit(event) {
|
async function handleAddAssetSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -128,41 +137,122 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
submitButton.disabled = true;
|
submitButton.disabled = true;
|
||||||
submitButton.innerHTML = '''<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Menyimpan...''';
|
submitButton.innerHTML = '''<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Menyimpan...''';
|
||||||
|
|
||||||
try {
|
const result = await fetchApi('add_asset.php', { method: 'POST', body: formData });
|
||||||
const response = await fetch(API_BASE + 'add_asset.php', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
if (result && result.success) {
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
showToast('Sukses', 'Aset baru berhasil ditambahkan.');
|
showToast('Sukses', 'Aset baru berhasil ditambahkan.');
|
||||||
addAssetForm.reset();
|
addAssetForm.reset();
|
||||||
addAssetForm.classList.remove('was-validated');
|
addAssetForm.classList.remove('was-validated');
|
||||||
bootstrap.Modal.getInstance(document.getElementById('addAssetModal')).hide();
|
bootstrap.Modal.getInstance(document.getElementById('addAssetModal')).hide();
|
||||||
loadAssets(); // Refresh the table
|
loadAssets();
|
||||||
} else {
|
} else if (result) {
|
||||||
showToast('Error', result.message || 'Terjadi kesalahan yang tidak diketahui.', 'error');
|
showToast('Error', result.message, 'error');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error('Error submitting form:', error);
|
|
||||||
showToast('Error', `Gagal mengirim data: ${error.message}`, 'error');
|
|
||||||
} finally {
|
|
||||||
submitButton.disabled = false;
|
submitButton.disabled = false;
|
||||||
submitButton.innerHTML = 'Simpan Aset';
|
submitButton.innerHTML = 'Simpan Aset';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the edit modal and populates it with asset data.
|
||||||
|
* @param {number} assetId - The ID of the asset to edit.
|
||||||
|
*/
|
||||||
|
async function openEditModal(assetId) {
|
||||||
|
const asset = await fetchApi(`get_asset.php?id=${assetId}`);
|
||||||
|
if (!asset) return;
|
||||||
|
|
||||||
|
document.getElementById('edit_id').value = asset.id;
|
||||||
|
document.getElementById('edit_nama_aset').value = asset.nama_aset;
|
||||||
|
document.getElementById('edit_id_kategori').value = asset.id_kategori;
|
||||||
|
document.getElementById('edit_id_kantor_lokasi').value = asset.id_kantor_lokasi;
|
||||||
|
document.getElementById('edit_spesifikasi').value = asset.spesifikasi;
|
||||||
|
document.getElementById('edit_tanggal_pembelian').value = asset.tanggal_pembelian;
|
||||||
|
document.getElementById('edit_harga_pembelian').value = asset.harga_pembelian;
|
||||||
|
document.getElementById('edit_vendor').value = asset.vendor;
|
||||||
|
document.getElementById('edit_status').value = asset.status;
|
||||||
|
|
||||||
|
editAssetModal.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the submission of the 'Edit Asset' form.
|
||||||
|
*/
|
||||||
|
async function handleEditAssetSubmit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (!editAssetForm.checkValidity()) {
|
||||||
|
editAssetForm.classList.add('was-validated');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData(editAssetForm);
|
||||||
|
const submitButton = editAssetForm.querySelector('button[type="submit"]');
|
||||||
|
submitButton.disabled = true;
|
||||||
|
submitButton.innerHTML = '''<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Menyimpan...''';
|
||||||
|
|
||||||
|
const result = await fetchApi('update_asset.php', { method: 'POST', body: formData });
|
||||||
|
|
||||||
|
if (result && result.success) {
|
||||||
|
showToast('Sukses', 'Aset berhasil diperbarui.');
|
||||||
|
editAssetModal.hide();
|
||||||
|
loadAssets();
|
||||||
|
} else if (result) {
|
||||||
|
showToast('Error', result.message, 'error');
|
||||||
|
}
|
||||||
|
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.innerHTML = 'Simpan Perubahan';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the deletion of an asset.
|
||||||
|
* @param {number} assetId - The ID of the asset to delete.
|
||||||
|
*/
|
||||||
|
async function handleDeleteAsset(assetId) {
|
||||||
|
if (!confirm('Anda yakin ingin menghapus aset ini? Tindakan ini tidak dapat dibatalkan.')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await fetchApi('delete_asset.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ id: assetId })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result && result.success) {
|
||||||
|
showToast('Sukses', 'Aset berhasil dihapus.');
|
||||||
|
loadAssets();
|
||||||
|
} else if (result) {
|
||||||
|
showToast('Error', result.message, 'error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- Initializations ---
|
// --- Initializations ---
|
||||||
|
|
||||||
feather.replace(); // Initial call for icons
|
feather.replace();
|
||||||
populateSelectOptions();
|
populateSelectOptions();
|
||||||
loadAssets();
|
loadAssets();
|
||||||
|
|
||||||
// --- Event Listeners ---
|
// --- Event Listeners ---
|
||||||
addAssetForm.addEventListener('submit', handleAddAssetSubmit);
|
addAssetForm.addEventListener('submit', handleAddAssetSubmit);
|
||||||
|
editAssetForm.addEventListener('submit', handleEditAssetSubmit);
|
||||||
|
|
||||||
|
assetsTableBody.addEventListener('click', function(event) {
|
||||||
|
const editButton = event.target.closest('.btn-edit');
|
||||||
|
const deleteButton = event.target.closest('.btn-delete');
|
||||||
|
|
||||||
|
if (editButton) {
|
||||||
|
const assetId = editButton.dataset.assetId;
|
||||||
|
openEditModal(assetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteButton) {
|
||||||
|
const assetId = deleteButton.dataset.assetId;
|
||||||
|
handleDeleteAsset(assetId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Add feather icon class for easier styling
|
// Add feather icon class for easier styling
|
||||||
document.querySelectorAll('i[data-feather]').forEach(el => {
|
document.querySelectorAll('i[data-feather]').forEach(el => {
|
||||||
|
|||||||
114
assets/js/offices.js
Normal file
114
assets/js/offices.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const officeModal = new bootstrap.Modal(document.getElementById('office-modal'));
|
||||||
|
const officeForm = document.getElementById('office-form');
|
||||||
|
const officeTable = document.getElementById('offices-table').getElementsByTagName('tbody')[0];
|
||||||
|
const modalLabel = document.getElementById('office-modal-label');
|
||||||
|
|
||||||
|
// Function to fetch and display offices
|
||||||
|
function loadOffices() {
|
||||||
|
fetch('api/get_offices.php')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
officeTable.innerHTML = '';
|
||||||
|
if(data.success) {
|
||||||
|
data.offices.forEach(office => {
|
||||||
|
const row = officeTable.insertRow();
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>${office.nama_kantor}</td>
|
||||||
|
<td>${office.alamat}</td>
|
||||||
|
<td>${office.tipe_kantor}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-sm btn-outline-secondary edit-btn" data-id="${office.id}"><i data-feather="edit-2" class="feather-sm"></i></button>
|
||||||
|
<button class="btn btn-sm btn-outline-danger delete-btn" data-id="${office.id}"><i data-feather="trash-2" class="feather-sm"></i></button>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
feather.replace();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error loading offices:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show modal for adding a new office
|
||||||
|
document.getElementById('btn-add-office').addEventListener('click', function () {
|
||||||
|
officeForm.reset();
|
||||||
|
document.getElementById('office-id').value = '';
|
||||||
|
modalLabel.textContent = 'Tambah Kantor Baru';
|
||||||
|
officeModal.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle form submission for both add and edit
|
||||||
|
officeForm.addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(officeForm);
|
||||||
|
const officeId = formData.get('id');
|
||||||
|
const url = officeId ? 'api/update_office.php' : 'api/add_office.php';
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
officeModal.hide();
|
||||||
|
loadOffices(); // Refresh the table
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + data.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Form submission error:', error));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle edit and delete button clicks
|
||||||
|
officeTable.addEventListener('click', function (e) {
|
||||||
|
const target = e.target.closest('button');
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
const id = target.dataset.id;
|
||||||
|
|
||||||
|
// Edit button
|
||||||
|
if (target.classList.contains('edit-btn')) {
|
||||||
|
// Fetch full details to edit
|
||||||
|
fetch(`api/get_offices.php?id=${id}`) // We can reuse get_offices and filter by id server-side
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success && data.offices.length > 0) {
|
||||||
|
const office = data.offices[0];
|
||||||
|
document.getElementById('office-id').value = office.id;
|
||||||
|
document.getElementById('nama_kantor').value = office.nama_kantor;
|
||||||
|
document.getElementById('alamat').value = office.alamat;
|
||||||
|
document.getElementById('tipe_kantor').value = office.tipe_kantor;
|
||||||
|
|
||||||
|
modalLabel.textContent = 'Edit Kantor';
|
||||||
|
officeModal.show();
|
||||||
|
} else {
|
||||||
|
alert('Error: Could not fetch office details.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete button
|
||||||
|
if (target.classList.contains('delete-btn')) {
|
||||||
|
if (confirm('Apakah Anda yakin ingin menghapus kantor ini? Ini mungkin akan mempengaruhi pengguna dan aset yang terkait.')) {
|
||||||
|
fetch('api/delete_office.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: `id=${id}`
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
loadOffices(); // Refresh the table
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + data.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Delete error:', error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial load of offices
|
||||||
|
loadOffices();
|
||||||
|
});
|
||||||
128
assets/js/users.js
Normal file
128
assets/js/users.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const userModal = new bootstrap.Modal(document.getElementById('user-modal'));
|
||||||
|
const userForm = document.getElementById('user-form');
|
||||||
|
const userTable = document.getElementById('users-table').getElementsByTagName('tbody')[0];
|
||||||
|
const modalLabel = document.getElementById('user-modal-label');
|
||||||
|
const passwordField = document.getElementById('password');
|
||||||
|
const passwordHelpText = passwordField.nextElementSibling;
|
||||||
|
|
||||||
|
// Function to fetch and display users
|
||||||
|
function loadUsers() {
|
||||||
|
fetch('api/get_users.php')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
userTable.innerHTML = '';
|
||||||
|
if(data.success) {
|
||||||
|
data.users.forEach(user => {
|
||||||
|
const row = userTable.insertRow();
|
||||||
|
row.innerHTML = `
|
||||||
|
<td>${user.nama_lengkap}</td>
|
||||||
|
<td>${user.email}</td>
|
||||||
|
<td>${user.role.replace('_', ' ')}</td>
|
||||||
|
<td>${user.nama_kantor || 'N/A'}</td>
|
||||||
|
<td>${new Date(user.created_at).toLocaleDateString()}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-sm btn-outline-secondary edit-btn" data-id="${user.id}"><i data-feather="edit-2" class="feather-sm"></i></button>
|
||||||
|
<button class="btn btn-sm btn-outline-danger delete-btn" data-id="${user.id}"><i data-feather="trash-2" class="feather-sm"></i></button>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
feather.replace();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error loading users:', error));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show modal for adding a new user
|
||||||
|
document.getElementById('btn-add-user').addEventListener('click', function () {
|
||||||
|
userForm.reset();
|
||||||
|
document.getElementById('user-id').value = '';
|
||||||
|
modalLabel.textContent = 'Tambah Pengguna Baru';
|
||||||
|
passwordField.setAttribute('required', 'required');
|
||||||
|
passwordHelpText.style.display = 'none';
|
||||||
|
userModal.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle form submission for both add and edit
|
||||||
|
userForm.addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData(userForm);
|
||||||
|
const userId = formData.get('id');
|
||||||
|
const url = userId ? 'api/update_user.php' : 'api/add_user.php';
|
||||||
|
|
||||||
|
// If password is empty on update, remove it from form data
|
||||||
|
if (userId && !formData.get('password')) {
|
||||||
|
formData.delete('password');
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
userModal.hide();
|
||||||
|
loadUsers(); // Refresh the table
|
||||||
|
// You can add a toast notification here for better UX
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + data.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Form submission error:', error));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle edit and delete button clicks
|
||||||
|
userTable.addEventListener('click', function (e) {
|
||||||
|
const target = e.target.closest('button');
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
const id = target.dataset.id;
|
||||||
|
|
||||||
|
// Edit button
|
||||||
|
if (target.classList.contains('edit-btn')) {
|
||||||
|
fetch(`api/get_user.php?id=${id}`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
const user = data.user;
|
||||||
|
document.getElementById('user-id').value = user.id;
|
||||||
|
document.getElementById('nama_lengkap').value = user.nama_lengkap;
|
||||||
|
document.getElementById('email').value = user.email;
|
||||||
|
document.getElementById('role').value = user.role;
|
||||||
|
document.getElementById('id_kantor').value = user.id_kantor || '';
|
||||||
|
|
||||||
|
modalLabel.textContent = 'Edit Pengguna';
|
||||||
|
passwordField.removeAttribute('required');
|
||||||
|
passwordHelpText.style.display = 'block';
|
||||||
|
userModal.show();
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + data.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete button
|
||||||
|
if (target.classList.contains('delete-btn')) {
|
||||||
|
if (confirm('Apakah Anda yakin ingin menghapus pengguna ini?')) {
|
||||||
|
fetch('api/delete_user.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
|
body: `id=${id}`
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
loadUsers(); // Refresh the table
|
||||||
|
} else {
|
||||||
|
alert('Error: ' + data.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Delete error:', error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initial load of users
|
||||||
|
loadUsers();
|
||||||
|
});
|
||||||
46
auth.php
Normal file
46
auth.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
$email = $_POST['email'] ?? '';
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
if (empty($email) || empty($password)) {
|
||||||
|
$_SESSION['login_error'] = 'Email dan password harus diisi.';
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
$user = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($user && password_verify($password, $user['password'])) {
|
||||||
|
// Regenerate session ID to prevent session fixation
|
||||||
|
session_regenerate_id(true);
|
||||||
|
|
||||||
|
$_SESSION['user_id'] = $user['id'];
|
||||||
|
$_SESSION['user_name'] = $user['nama_lengkap'];
|
||||||
|
$_SESSION['user_role'] = $user['role'];
|
||||||
|
$_SESSION['user_office_id'] = $user['id_kantor'];
|
||||||
|
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$_SESSION['login_error'] = 'Email atau password salah.';
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// In a real app, you would log this error
|
||||||
|
$_SESSION['login_error'] = 'Terjadi kesalahan pada database.';
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
31
db/setup.php
31
db/setup.php
@ -50,6 +50,21 @@ try {
|
|||||||
UNIQUE KEY `kode_kategori` (`kode_kategori`)
|
UNIQUE KEY `kode_kategori` (`kode_kategori`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `users` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`nama_lengkap` varchar(255) NOT NULL,
|
||||||
|
`email` varchar(255) NOT NULL,
|
||||||
|
`password` varchar(255) NOT NULL,
|
||||||
|
`role` enum('super_admin','admin_cabang','pegawai') NOT NULL DEFAULT 'pegawai',
|
||||||
|
`id_kantor` int(11) DEFAULT NULL,
|
||||||
|
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
|
`updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `email` (`email`),
|
||||||
|
KEY `id_kantor` (`id_kantor`),
|
||||||
|
CONSTRAINT `users_ibfk_1` FOREIGN KEY (`id_kantor`) REFERENCES `kantor` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS `aset` (
|
CREATE TABLE IF NOT EXISTS `aset` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
`kode_aset` varchar(255) NOT NULL,
|
`kode_aset` varchar(255) NOT NULL,
|
||||||
@ -68,13 +83,15 @@ try {
|
|||||||
UNIQUE KEY `kode_aset` (`kode_aset`),
|
UNIQUE KEY `kode_aset` (`kode_aset`),
|
||||||
KEY `id_kategori` (`id_kategori`),
|
KEY `id_kategori` (`id_kategori`),
|
||||||
KEY `id_kantor_lokasi` (`id_kantor_lokasi`),
|
KEY `id_kantor_lokasi` (`id_kantor_lokasi`),
|
||||||
|
KEY `id_pengguna_penanggung_jawab` (`id_pengguna_penanggung_jawab`),
|
||||||
CONSTRAINT `aset_ibfk_1` FOREIGN KEY (`id_kategori`) REFERENCES `kategori_aset` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE,
|
CONSTRAINT `aset_ibfk_1` FOREIGN KEY (`id_kategori`) REFERENCES `kategori_aset` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
CONSTRAINT `aset_ibfk_2` FOREIGN KEY (`id_kantor_lokasi`) REFERENCES `kantor` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
|
CONSTRAINT `aset_ibfk_2` FOREIGN KEY (`id_kantor_lokasi`) REFERENCES `kantor` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `aset_ibfk_3` FOREIGN KEY (`id_pengguna_penanggung_jawab`) REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
";
|
";
|
||||||
|
|
||||||
$pdo->exec($sql);
|
$pdo->exec($sql);
|
||||||
echo "Tables `kantor`, `kategori_aset`, and `aset` created or already exist.\n";
|
echo "Tables `kantor`, `kategori_aset`, `users`, and `aset` created or already exist.\n";
|
||||||
|
|
||||||
// --- Seed initial data if tables are empty ---
|
// --- Seed initial data if tables are empty ---
|
||||||
$stmt = $pdo->query("SELECT COUNT(*) FROM `kantor`");
|
$stmt = $pdo->query("SELECT COUNT(*) FROM `kantor`");
|
||||||
@ -100,6 +117,16 @@ try {
|
|||||||
echo "Seeded `kategori_aset` table with initial data.\n";
|
echo "Seeded `kategori_aset` table with initial data.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SELECT COUNT(*) FROM `users`");
|
||||||
|
if ($stmt->fetchColumn() == 0) {
|
||||||
|
$hashed_password = password_hash('password', PASSWORD_DEFAULT);
|
||||||
|
$pdo->exec("
|
||||||
|
INSERT INTO `users` (`nama_lengkap`, `email`, `password`, `role`, `id_kantor`) VALUES
|
||||||
|
('Super Admin', 'admin@example.com', '{$hashed_password}', 'super_admin', 1);
|
||||||
|
");
|
||||||
|
echo "Seeded `users` table with a super admin user.\n";
|
||||||
|
}
|
||||||
|
|
||||||
// Seed a sample asset
|
// Seed a sample asset
|
||||||
$stmt = $pdo->query("SELECT COUNT(*) FROM `aset`");
|
$stmt = $pdo->query("SELECT COUNT(*) FROM `aset`");
|
||||||
if ($stmt->fetchColumn() == 0) {
|
if ($stmt->fetchColumn() == 0) {
|
||||||
|
|||||||
116
index.php
116
index.php
@ -1,3 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
// If the user is not logged in, redirect to the login page.
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="id">
|
<html lang="id">
|
||||||
<head>
|
<head>
|
||||||
@ -28,10 +36,35 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<header class="app-header">
|
<header class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
|
||||||
<div class="container">
|
<div class="container-fluid">
|
||||||
<h1>Data Aset Perusahaan</h1>
|
<a class="navbar-brand" href="index.php">Manajemen Aset</a>
|
||||||
<p class="lead">Selamat datang di sistem manajemen aset terpusat.</p>
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<?php if (isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'super_admin'): ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="index.php">Aset</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="users.php">Pengguna</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="offices.php">Kantor</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i data-feather="user" class="me-1"></i> <?php echo htmlspecialchars($_SESSION['user_name']); ?>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
|
||||||
|
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@ -147,6 +180,81 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Edit Asset Modal -->
|
||||||
|
<div class="modal fade" id="editAssetModal" tabindex="-1" aria-labelledby="editAssetModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="editAssetModalLabel">Edit Aset</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="editAssetForm" class="needs-validation" novalidate>
|
||||||
|
<input type="hidden" id="edit_id" name="id">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 mb-3">
|
||||||
|
<label for="edit_nama_aset" class="form-label">Nama Aset</label>
|
||||||
|
<input type="text" class="form-control" id="edit_nama_aset" name="nama_aset" required>
|
||||||
|
<div class="invalid-feedback">Nama aset tidak boleh kosong.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_id_kategori" class="form-label">Kategori</label>
|
||||||
|
<select class="form-select" id="edit_id_kategori" name="id_kategori" required>
|
||||||
|
<!-- Options will be populated by JS -->
|
||||||
|
</select>
|
||||||
|
<div class="invalid-feedback">Silakan pilih kategori.</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_id_kantor_lokasi" class="form-label">Lokasi Kantor</label>
|
||||||
|
<select class="form-select" id="edit_id_kantor_lokasi" name="id_kantor_lokasi" required>
|
||||||
|
<!-- Options will be populated by JS -->
|
||||||
|
</select>
|
||||||
|
<div class="invalid-feedback">Silakan pilih lokasi.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="edit_spesifikasi" class="form-label">Spesifikasi</label>
|
||||||
|
<textarea class="form-control" id="edit_spesifikasi" name="spesifikasi" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_tanggal_pembelian" class="form-label">Tanggal Pembelian</label>
|
||||||
|
<input type="date" class="form-control" id="edit_tanggal_pembelian" name="tanggal_pembelian" required>
|
||||||
|
<div class="invalid-feedback">Tanggal pembelian tidak boleh kosong.</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_harga_pembelian" class="form-label">Harga Pembelian (Rp)</label>
|
||||||
|
<input type="number" class="form-control" id="edit_harga_pembelian" name="harga_pembelian" step="0.01" required>
|
||||||
|
<div class="invalid-feedback">Harga pembelian tidak boleh kosong.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_vendor" class="form-label">Vendor</label>
|
||||||
|
<input type="text" class="form-control" id="edit_vendor" name="vendor">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="edit_status" class="form-label">Status</label>
|
||||||
|
<select class="form-select" id="edit_status" name="status" required>
|
||||||
|
<option value="Tersedia">Tersedia</option>
|
||||||
|
<option value="Digunakan">Digunakan</option>
|
||||||
|
<option value="Perbaikan">Perbaikan</option>
|
||||||
|
<option value="Dihapuskan">Dihapuskan</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Simpan Perubahan</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Toast container -->
|
<!-- Toast container -->
|
||||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||||
<div id="notificationToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
<div id="notificationToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||||
|
|||||||
64
login.php
Normal file
64
login.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
if (isset($_SESSION['login_error'])) {
|
||||||
|
$error = $_SESSION['login_error'];
|
||||||
|
unset($_SESSION['login_error']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="id">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login - Manajemen Aset</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #F4F7F9;
|
||||||
|
}
|
||||||
|
.login-card {
|
||||||
|
max-width: 400px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card login-card shadow-sm">
|
||||||
|
<div class="card-body p-5">
|
||||||
|
<h3 class="card-title text-center mb-4">Login Sistem</h3>
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<form action="auth.php" method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Alamat Email</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="submit" class="btn btn-primary">Login</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
21
logout.php
Normal file
21
logout.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Unset all of the session variables
|
||||||
|
$_SESSION = [];
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
129
offices.php
Normal file
129
offices.php
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ($_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
header('Location: index.php?error=unauthorized');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="id">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Manajemen Kantor - Sistem Manajemen Aset</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.css" rel="stylesheet">
|
||||||
|
<link href="assets/css/custom.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="main-content flex-grow-1">
|
||||||
|
<header class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="index.php">Manajemen Aset</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<?php if ($_SESSION['user_role'] === 'super_admin'): ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="index.php">Aset</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="users.php">Pengguna</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="offices.php">Kantor</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i data-feather="user" class="me-1"></i> <?php echo htmlspecialchars($_SESSION['user_name']); ?>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
|
||||||
|
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container-fluid mt-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1 class="h2">Manajemen Kantor</h1>
|
||||||
|
<button class="btn btn-primary" id="btn-add-office"><i data-feather="plus" class="me-1"></i> Tambah Kantor Baru</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover" id="offices-table">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Nama Kantor</th>
|
||||||
|
<th>Alamat</th>
|
||||||
|
<th>Tipe</th>
|
||||||
|
<th>Aksi</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- Office data will be loaded here by JavaScript -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add/Edit Office Modal -->
|
||||||
|
<div class="modal fade" id="office-modal" tabindex="-1" aria-labelledby="office-modal-label" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="office-modal-label">Tambah Kantor Baru</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="office-form">
|
||||||
|
<input type="hidden" id="office-id" name="id">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="nama_kantor" class="form-label">Nama Kantor</label>
|
||||||
|
<input type="text" class="form-control" id="nama_kantor" name="nama_kantor" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="alamat" class="form-label">Alamat</label>
|
||||||
|
<textarea class="form-control" id="alamat" name="alamat" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="tipe_kantor" class="form-label">Tipe Kantor</label>
|
||||||
|
<select class="form-select" id="tipe_kantor" name="tipe_kantor" required>
|
||||||
|
<option value="pusat">Pusat</option>
|
||||||
|
<option value="cabang" selected>Cabang</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Simpan</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
||||||
|
<script>
|
||||||
|
feather.replace();
|
||||||
|
</script>
|
||||||
|
<script src="assets/js/offices.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
165
users.php
Normal file
165
users.php
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ($_SESSION['user_role'] !== 'super_admin') {
|
||||||
|
// Optionally, redirect to a "not authorized" page or back to index
|
||||||
|
header('Location: index.php?error=unauthorized');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include database configuration
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// Fetch options for form selects (offices)
|
||||||
|
try {
|
||||||
|
$stmt_offices = db()->query("SELECT id, nama_kantor FROM kantor ORDER BY nama_kantor");
|
||||||
|
$offices = $stmt_offices->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Handle error, maybe log it and show a generic message
|
||||||
|
$offices = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="id">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Manajemen Pengguna - Sistem Manajemen Aset</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.css" rel="stylesheet">
|
||||||
|
<link href="assets/css/custom.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="d-flex">
|
||||||
|
<!-- Sidebar can be added here if needed -->
|
||||||
|
<div class="main-content flex-grow-1">
|
||||||
|
<header class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="index.php">Manajemen Aset</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<?php if ($_SESSION['user_role'] === 'super_admin'): ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="index.php">Aset</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" href="users.php">Pengguna</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="offices.php">Kantor</a>
|
||||||
|
</li>
|
||||||
|
<?php endif; ?>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i data-feather="user" class="me-1"></i> <?php echo htmlspecialchars($_SESSION['user_name']); ?>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
|
||||||
|
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container-fluid mt-4">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1 class="h2">Manajemen Pengguna</h1>
|
||||||
|
<button class="btn btn-primary" id="btn-add-user"><i data-feather="plus" class="me-1"></i> Tambah Pengguna Baru</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover" id="users-table">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Nama Lengkap</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Role</th>
|
||||||
|
<th>Kantor</th>
|
||||||
|
<th>Tgl Dibuat</th>
|
||||||
|
<th>Aksi</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<!-- User data will be loaded here by JavaScript -->
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add/Edit User Modal -->
|
||||||
|
<div class="modal fade" id="user-modal" tabindex="-1" aria-labelledby="user-modal-label" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="user-modal-label">Tambah Pengguna Baru</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="user-form">
|
||||||
|
<input type="hidden" id="user-id" name="id">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="nama_lengkap" class="form-label">Nama Lengkap</label>
|
||||||
|
<input type="text" class="form-control" id="nama_lengkap" name="nama_lengkap" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="password" name="password">
|
||||||
|
<small class="form-text text-muted">Kosongkan jika tidak ingin mengubah password.</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label for="role" class="form-label">Role</label>
|
||||||
|
<select class="form-select" id="role" name="role" required>
|
||||||
|
<option value="super_admin">Super Admin</option>
|
||||||
|
<option value="admin_cabang">Admin Cabang</option>
|
||||||
|
<option value="pegawai" selected>Pegawai</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="id_kantor" class="form-label">Kantor</label>
|
||||||
|
<select class="form-select" id="id_kantor" name="id_kantor">
|
||||||
|
<option value="">Tidak Ditugaskan</option>
|
||||||
|
<?php foreach ($offices as $office): ?>
|
||||||
|
<option value="<?php echo $office['id']; ?>"><?php echo htmlspecialchars($office['nama_kantor']); ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Simpan</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
||||||
|
<script>
|
||||||
|
feather.replace();
|
||||||
|
</script>
|
||||||
|
<script src="assets/js/users.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user