diff --git a/.env b/.env new file mode 100644 index 0000000000000000000000000000000000000000..907206c37ef7e99e644322733e373adf4597d2db --- /dev/null +++ b/.env @@ -0,0 +1,5 @@ +MYSQL_ROOT_PASSWORD=m19e11 +MYSQL_DATABASE=tubes1_WBD +MYSQL_USER=tubes1-WBD +MYSQL_PASSWORD=1234 + diff --git a/README.md b/README.md index 5614db31c485058c8277b93c72a82d02c6d04a21..e5cc5ed1ceda660359e98f561d2ddfb932fe63de 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,163 @@ -## Panduan Pengerjaan -Berikut adalah hal yang harus diperhatikan untuk pengumpulan tugas ini: -1. Buatlah grup pada Gitlab dengan format "IF3110-2023-01-XX", dengan XX adalah nomor kelompok (untuk K1 dan K2) atau kode kelompok (untuk K3). -2. Tambahkan anggota tim pada grup anda. -3. **Fork** pada repository ini dengan organisasi yang telah dibuat. -4. Ubah hak akses repository hasil Fork anda menjadi **private**. -5. Hal-hal yang harus diperhatikan. - * Silakan commit pada repository anda (hasil fork) - * Lakukan beberapa commit dengan pesan yang bermakna, contoh: “add register formâ€, “fix logout bugâ€, jangan seperti “finalâ€, “benerin dikitâ€, “fix bugâ€. - * Disarankan untuk tidak melakukan commit dengan perubahan yang besar karena akan mempengaruhi penilaian (contoh: hanya melakukan satu commit kemudian dikumpulkan). - * Commit dari setiap anggota tim akan mempengaruhi penilaian. - * Jadi, setiap anggota tim harus melakukan commit yang berpengaruh terhadap proses pembuatan aplikasi. - * Sebagai panduan bisa mengikuti [semantic commit](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716). -6. Buatlah file README yang berisi: - * Deskripsi aplikasi web - * Daftar requirement - * Cara instalasi - * Cara menjalankan server - * Screenshot tampilan aplikasi (tidak perlu semua kasus, minimal 1 per halaman), dan - * Penjelasan mengenai pembagian tugas masing-masing anggota (lihat formatnya pada bagian pembagian tugas). +# IF3110 - Tugas Besar WBD 1 + +## TicketKu: Ticket Management System + +## Kelompok 18: + +- 13521075 - Muhammad Rifko Favian +- 13521111 - Tabitha Permalla +- 13521151 - Vanessa Rebecca Wiyono + +## Deskripsi Aplikasi web + +Aplikasi web yang dibangun merupakan sebuah sistem informasi yang digunakan untuk melakukan manajemen / pengelolaan stok tiket. Dengan spesifikasi sebagai berikut: + +1. Implementasi client-side menggunakan Javascript, HTML, dan CSS. +2. Implementasi server-side menggunakan PHP murni tanpa framework +3. Implementasi basis data menggunakan MySQL +4. Sistem dibuat dengan struktur MVC + +Fitur yang dimiliki aplikasi antara lain: + +1. Atentikasi pengguna agar dapat mengakses seluruh fitur lainnya. + Pengguna dibedakan menjadi 2 kategori: user dan admin. +2. Admin dapat melakukan penambahan event +3. Admin dapat melakukan edit event +5. User dapat melihat daftar event +6. User dapat melakukan pembelian tiket suatu event +7. User dapat melihat history pembelian tiket +8. User dapat melakukan edit profile + +Spesifikasi bonus yang tersedia yaitu: + +1. Responsive Design +2. Docker + +## Daftar Requirement +- PHP 8 + +Untuk menjalankan server secara lokal tanpa Docker: +- XAMPP + +Untuk menjalankan dengan Docker: +- OS Ubuntu / Windows dengan WSL2 +- Docker +- Docker Compose + +## Cara Instalasi + +### XAMPP + +**Aktivasi MySQL pada PHP** + +1. Buka php.ini in C:\xampp\php +2. Tambahkan / uncomment kode ini +```bash +extension=mysqli +``` +3. Pastikan libmysql.dll dapat diakses. + +**Aktivasi PDO MySQL pada PHP** +1. Buka file php.ini di C:\xampp\php. +2. Tambahkan / uncomment kode ini +```bash +extension=pdo_mysql +``` + +## Cara Menjalankan server + +### XAMPP + +1. Clone repository ini pada directory htdocs XAMPP anda. Contohnya: `C:\xampp\htdocs\` +2. Buka XAMPP Control Panel dan Start service Apache +3. Aplikasi akan dijalankan pada `localhost/tugas-besar-1` + +### Docker + +1. Clone repository ini +2. Masuk ke directory utama (root) dari repository +3. Untuk menjalankan aplikasi: + +```bash +docker-compose up --build +``` + +4. Container akan berjalan dengan nama `tubes-1` +5. Aplikasi akan berjalan pada `localhost:8008` +6. Untuk menginisialisasi atau me-reset data dummy, pergi ke laman 'localhost:8008/db/init.php' +7. Untuk menghentikan aplikasi, jalankan: + +```bash +docker-compose stop +``` + + +## Screenshot Tampilan Aplikasi +- Login +<img src="./assets/readme/login.png" alt="Login"><br> +- Register +<img src="./assets/readme/register.png" alt="Register"><br> +- User Homepage +<img src="./assets/readme/user_homepage.png" alt="User Home"><br> +- Admin Homepage +<img src="./assets/readme/admin_homepage.png" alt="Admin Home"><br> +- Create Event +<img src="./assets/readme/create_event.png" alt="Create Event"><br> +- Update Event +<img src="./assets/readme/edit_event.png" alt="Update Event"><br> +- Delete Event +<img src="./assets/readme/delete_event.png" alt="Delete Event"><br> +- Event Details / Purchase Ticket +<img src="./assets/readme/purchase_ticket.png" alt="Purchase Ticket"><br> +- Purchase History +<img src="./assets/readme/history_page.png" alt="History"><br> +- View Profile +<img src="./assets/readme/view_profile.png" alt="Lihat Artikel"><br> +- Edit Profile +<img src="./assets/readme/edit_profile.png" alt="Lihat Artikel"><br> + + +## Pembagian Tugas + +### Bonus + +| Item | NIM | +| ------------------------- | ------------------ | +| Bonus: Docker | 13521151 | + +#### Server-side + +| Item | NIM | +| ---------------------- | ------------------ | +| Login | 13521111 | +| Register | 13521111 | +| Dashboard | 13521111, 13521151 | +| Pencarian Event | 13521151 | +| Daftar Event | 13521151 | +| Tambah Event | 13521075, 13521151 | +| Edit Event | 13521075, 13521151 | +| Delete Event | 13521075, 13521151 | +| Pembelian tiket | 13521111 | +| Setup Basis Data | 13521151 | +| History | 13521151 | +| Lihat profile | 13521151 | +| Navbar | 13521111, 13521151 | +| Footer | 13521111, 13521151 | + +#### Client-side + +| Item | NIM | +| ------------------------ | ---------------------------- | +| Init Directories, Styles | 13521111, 13521151 | +| Login | 13521075, 13521111 | +| Register | 13521075, 13521111 | +| Dashboard | 13521111, 13521151 | +| Pencarian Event | 13521111, 13521151 | +| Daftar Event | 13521111, 13521151 | +| Pembelian Tiket | 13521111 | +| Tambah Event | 13521075, 13521151 | +| Edit Event | 13521075, 13521151 | +| Lihat profile | 13521111, 13521151 | +| Edit profile | 13521111, 13521151 | +| Navbar | 13521111, 13521151 | +| Footer | 13521111, 13521151 | diff --git a/app/Controllers/EventController.php b/app/Controllers/EventController.php index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2961a637e58138af8df7c7c26365bee9aff3b89e 100644 --- a/app/Controllers/EventController.php +++ b/app/Controllers/EventController.php @@ -0,0 +1,137 @@ +<?php +// app/Controllers/EventController.php + +ob_start(); +require_once(__DIR__ . '/../Models/Event.php'); +require_once(__DIR__ . '/TicketController.php'); + + +class EventController { + private $eventModel; + + public function __construct() { + $this->eventModel = new EventModel(); + } + + public function createEvent($event_name, $stock, $event_price, $event_date, $event_location, $gambar, $vid){ + $success = $this->eventModel->createEvent($event_name, $stock, $event_price, $event_date, $event_location, $gambar, $vid); + + if ($success) { + $ticketController = new TicketController(); + $event_id = $this->eventModel->getLastEventId(); + for ($i = 0; $i < $stock; $i++) { + $ticketController->createTicket($event_name . " Ticket", $event_id); + } + } + return $success; + } + + public function getEvent($eventId) { + return $this->eventModel->getEvent($eventId); + } + + public function updateEvent($eventId, $event_name, $event_price, $event_date, $event_location, $gambar, $vid){ + $eventModel = new EventModel(); + $eventModel->updateEvent($eventId, $event_name, $event_price, $event_date, $event_location, $gambar, $vid); + + $ticketController = new TicketController(); + $ticketController->updateTicketbyEventId($eventId, $event_name . " Ticket"); + } + + public function deleteEvent($eventId) { + return $this->eventModel->deleteEvent($eventId); + } + + public function getAllEvents() { + return $this->eventModel->getAllEvents(); + } + + public function paginateEvents($events = [], $page = 1, $pagesize = 10) { + $totalEvents = count($events); + $maxpage = ceil($totalEvents / $pagesize); + if ($page > $maxpage) { + $page = $maxpage; + } + $offset = $pagesize * ($page - 1); + $selectedEvents = array_slice($events, $offset, $pagesize); + + return [ + 'events' => $selectedEvents, + 'total' => $totalEvents, + 'page' => $page, + 'maxpage' => $maxpage, + ]; + } + + + + public function searchEvents($searchQuery, $sortKey, $minStock = null) { + try { + return $this->eventModel->searchEvents($searchQuery, $sortKey, $minStock); + } catch (Exception $e) { + echo "Error searching events: " . $e->getMessage(); + return []; + } + } + + public function handleRequest() { + if (isset($_POST['eventAction'])) { + if ($_POST['eventAction'] === 'createEvent') { + unset($_POST['eventAction']); + $eventName = $_POST['eventName']; + $eventStock = $_POST['eventStock']; + $eventPrice = $_POST['eventPrice']; + $eventDate = $_POST['eventDate']; + $eventLocation = $_POST['eventLocation']; + + $imageFilePath = isset($_FILES['gambar']) && $_FILES['gambar']['error'] === 0 ? $_FILES['gambar'] : null; + $vidFilePath = isset($_FILES['vid']) && $_FILES['vid']['error'] === 0 ? $_FILES['vid'] : null; + + $success = $this->createEvent($eventName, $eventStock, $eventPrice, $eventDate, $eventLocation, $imageFilePath, $vidFilePath); + if($success){ + header("Location:Views/home/home.php"); + ob_end_flush(); + } else { + header("Location:Views/event/create.php"); + ob_end_flush(); + } + } + elseif ($_POST['eventAction'] === 'updateEvent') { + unset($_POST['eventAction']); + $eventId = $_POST['eventId']; + $eventName = $_POST['eventName']; + $eventPrice = $_POST['eventPrice']; + $eventDate = $_POST['eventDate']; + $eventLocation = $_POST['eventLocation']; + + $imageFilePath = isset($_FILES['gambar']) && $_FILES['gambar']['error'] === 0 ? $_FILES['gambar'] : null; + $vidFilePath = isset($_FILES['vid']) && $_FILES['vid']['error'] === 0 ? $_FILES['vid'] : null; + + $this->updateEvent($eventId, $eventName, $eventPrice, $eventDate, $eventLocation, $imageFilePath, $vidFilePath); + header("Location:Views/home/home.php"); + ob_end_flush(); + } + elseif ($_POST['eventAction'] === 'deleteEvent') { + unset($_POST['eventAction']); + $eventId = $_POST['eventId']; + $this->deleteEvent($eventId); + header("Location:Views/home/home.php"); + ob_end_flush(); + } + } else { + // other + } + if (isset($_GET['eventAction'])) { + if ($_GET['eventAction'] === 'getEvent') { + $eventId = $_GET['eventId']; + unset($_GET['eventAction']); + unset($_GET['eventId']); + echo json_encode($this->getEvent($eventId)); + } else { + // other + } + } + } + +} +?> diff --git a/app/Controllers/PembelianController.php b/app/Controllers/PembelianController.php index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9f5526def638e8436eec7807ffeff1778b9b542e 100644 --- a/app/Controllers/PembelianController.php +++ b/app/Controllers/PembelianController.php @@ -0,0 +1,83 @@ +<?php +// app/controllers/pembelianController.php + +ob_start(); +// require_once 'Pembelian.php'; +require_once(__DIR__ . '/../Models/Pembelian.php'); + +class PembelianController { + private $pembelianModel; + + + public function __construct() { + $this->pembelianModel = new PembelianModel(); + } + + public function createPembelian($ticketId, $userId, $createdTime) { + $pembelianModel = new PembelianModel(); + return $this->pembelianModel->createPembelian($ticketId, $userId, $createdTime); + } + + public function getPembelian($pembelianId) { + return $this->pembelianModel->getPembelian($pembelianId); + } + + public function updatePembelian($pembelianId, $ticketId, $userId, $createdTime) { + return $this->pembelianModel->updatePembelian($pembelianId, $ticketId, $userId, $createdTime); + } + + public function deletePembelian($pembelianId) { + return $this->pembelianModel->deletePembelian($pembelianId); + } + + public function getAllPembelian(){ + return $this->pembelianModel->getAllPembelian(); + } + + public function getAllPembelianWithDetails(){ + return $this->pembelianModel->getAllPembelianWithDetails(); + } + + public function getPembelianByUserId($userId) { + return $this->pembelianModel->getPembelianByUserId($userId); + } + + public function paginateHistory($pembelian=[],$page=1,$pagesize=10) { + $totalPembelian = count($pembelian); + $maxpage = ceil($totalPembelian / $pagesize); + if ($page > $maxpage) { + $page = $maxpage; + } + $offset = $pagesize * ($page - 1); + $selectedPembelian = array_slice($pembelian, $offset, $pagesize); + + return [ + 'pembelian' => $selectedPembelian, + 'total' => $totalPembelian, + 'page' => $page, + 'maxpage' => $maxpage, + ]; + } + + public function purchaseTicket($ticketId,$userId) { + $createdTime = date("Y-m-d H:i:s"); + $this->createPembelian($ticketId,$userId,$createdTime); + } + + public function handleRequest() { + if (isset($_POST['purchaseAction'])) { + if ($_POST['purchaseAction'] === 'purchaseTicket') { + unset($_POST['purchaseAction']); + $ticketId = $_POST['ticketId']; + $userId = $_POST['userId']; + $this->purchaseTicket($ticketId,$userId); + header("Location: /app/Views/history/history.php"); + ob_end_flush(); + } else { + // others + } + } + } + +} +?> diff --git a/app/Controllers/TicketController.php b/app/Controllers/TicketController.php index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..962586f10ebe910073345edf0e57faa66d2b421c 100644 --- a/app/Controllers/TicketController.php +++ b/app/Controllers/TicketController.php @@ -0,0 +1,58 @@ +<?php +// app/controllers/ticketController.php +ob_start(); +// require_once 'Ticket.php'; +require_once(__DIR__ . '/../Models/Ticket.php'); + +class TicketController { + private $ticketModel; + + public function __construct() { + $this->ticketModel = new TicketModel(); + } + + public function createTicket($name, $eventId) { + return $this->ticketModel->createTicket($name, $eventId); + } + + public function getTicket($ticketId) { + return $this->ticketModel->getTicket($ticketId); + } + + public function updateTicket($ticketId, $name, $eventId) { + return $this->ticketModel->updateTicket($ticketId, $name, $eventId); + } + + public function updateTicketByEventId($eventId, $name) { + return $this->ticketModel->updateTicketByEventId($eventId, $name); + } + + public function deleteTicket($ticketId) { + return $this->ticketModel->deleteTicket($ticketId); + } + + // public function previewTicket($ticketId) { + // $ticket = $this->getTicket($ticketId); + + // include(__DIR__ . '/../Views/pembelian/pembelian.php'); + // } + + public function getEventTicketDetails($eventId) { + $ticket = $this->ticketModel->getSmallestAvailableTicket($eventId); + include(__DIR__ . '/../Views/pembelian/pembelian.php'); + } + + public function handleRequest() { + if (isset($_GET['ticketAction'])) { + if ($_GET['ticketAction'] === 'getTicket') { + unset($_GET['ticketAction']); + $eventId = $_GET['eventId']; + $this->getEventTicketDetails($eventId); + // header("Location: Views/pembelian/pembelian.php"); + } else { + // Handle other actions here, if needed + } + } + } +} +?> diff --git a/app/Controllers/UserController.php b/app/Controllers/UserController.php index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3ae2e6c4c5ede827e7bf481525c9e222bda0eafd 100644 --- a/app/Controllers/UserController.php +++ b/app/Controllers/UserController.php @@ -0,0 +1,173 @@ +<?php +// app/controllers/userController.php +ob_start(); +require_once(__DIR__ . '/../Models/User.php'); + + +class UserController { + private $userModel; + + public function __construct() { + $this->userModel = new UserModel(); + } + + public function createUser($name, $username, $email, $hashedPass, $isAdmin) { + return $this->userModel->createUser($name, $username, $email, $hashedPass, $isAdmin); + } + + public function getUser($userId) { + return $this->userModel->getUser($userId); + } + + public function updateUser($userId, $name, $username, $email, $hashedPass, $isAdmin) { + return $this->userModel->updateUser($userId, $name, $username, $email, $hashedPass, $isAdmin); + } + + public function deleteUser($userId) { + return $this->userModel->deleteUser($userId); + } + + public function getAllUsers() { + return $this->userModel->getAllUsers(); + } + + public function registerUser($name, $username, $email, $password, $isAdmin) { + return $this->createUser($name, $username, $email, password_hash($password,PASSWORD_DEFAULT), $isAdmin); + } + + public function loginByEmail($email,$password) { + $user = $this->userModel->getUserByEmail($email); + if ($user !== false && password_verify($password, $user['user_hashedPass'])) { + session_start(); + $_SESSION["user_id"] = $user['user_ID']; + return "success"; + } else { + return "wrong credentials"; + } + } + + public function loginByUsername($username,$password) { + $user = $this->userModel->getUserByUsername($username); + if ($user !== false && password_verify($password, $user['user_hashedPass'])) { + session_start(); + $_SESSION["user_id"] = $user['user_ID']; + return "success"; + } else { + session_start(); + return "wrong credentials"; + } + } + + public function logout() { + session_start(); + if (isset($_SESSION["user_id"])) { + unset($_SESSION["user_id"]); + session_destroy(); + return "logout successful"; + } else { + return "user not logged in"; + } + } + + public function editProfile($user_id, $name, $username, $email) { + return $this->userModel->editProfile($user_id, $name, $username, $email); + } + + public function handleRequest() { + if (isset($_POST['userAction'])) { + if ($_POST['userAction'] === 'login') { + unset($_POST['userAction']); + $emailOrUsername = $_POST['loginIdentifier']; + $password = $_POST['loginPassword']; + if (filter_var($emailOrUsername, FILTER_VALIDATE_EMAIL)){ + if ($this->loginByEmail($emailOrUsername, $password) === "success") { + header("Location:Views/home/home.php"); + ob_end_flush(); + } else { + session_start(); + $_SESSION['message'] = "wrong credentials"; + header("Location:Views/login/login.php"); + ob_end_flush(); + } + } else { + if ($this->loginByUsername($emailOrUsername,$password) === "success") { + header("Location:Views/home/home.php"); + ob_end_flush(); + } else { + session_start(); + $_SESSION['message'] = "wrong credentials"; + header("Location:Views/login/login.php"); + ob_end_flush(); + } + } + } elseif ($_POST['userAction'] === 'createUser') { + unset($_POST['userAction']); + $userName = $_POST['userName']; + $userUsername = $_POST['userUsername']; + $userEmail = $_POST['userEmail']; + $userPassword = $_POST['userPassword']; + $isAdmin = isset($_POST['isAdmin']) ? 1 : 0; + $response = $this->registerUser($userName,$userUsername,$userEmail,$userPassword,$isAdmin); + $success = $response['success']; + if($success){ + session_start(); + $_SESSION['message'] = "You have successfully registered. Please log in"; + header("Location:Views/login/login.php"); + ob_end_flush(); + } else { + session_start(); + $_SESSION['error_message'] = $response['message']; + header("Location:Views/login/register.php"); + ob_end_flush(); + } + } elseif ($_POST['userAction'] === 'logout') { + unset($_POST['userAction']); + $this->logout(); + session_start(); + $_SESSION['message'] = "User logged out successfully. Please log in again."; + header("Location:Views/login/login.php"); + ob_end_flush(); + } elseif ($_POST['userAction'] === 'viewProfile') { + unset($_POST['userAction']); + // Display existing data + if (isset($_SESSION['user_id'])) { + $userId = $_SESSION['user_id']; + echo "User ID from session: " . $userId; // Debugging + + $userData = $this->getUser($userId); + + if ($userData !== null) { + $_SESSION['userData'] = $userData; + header("Location:Views/profile/view_profile.php"); + ob_end_flush(); + } else { + echo "User data not available."; + } + } else { + echo "User ID not set in session."; // Debugging + } + + } elseif ($_POST['userAction'] === 'editProfile') { + unset($_POST['userAction']); + $userId = $_SESSION['user_id']; + $newUserName = $_POST['user_name']; + $newUsername = $_POST['user_username']; + $newUserEmail = $_POST['user_email']; + + $this->editProfile($_SESSION['user_id'], $newUserName, $newUsername, $newUserEmail); + + header('Location:Views/profile/view_profile.php'); + ob_end_flush(); + } + } + if (isset($_GET['userAction'])) { + if ($_GET['userAction'] === 'getUser') { + unset($_GET['userAction']); + session_start(); + echo json_encode($this->getUser($_SESSION['user_id'])); + } + } + } + +} +?> \ No newline at end of file diff --git a/app/Models/Event.php b/app/Models/Event.php index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..94ec0d38200f8e5d4b30acc731426dd08ce58f3c 100644 --- a/app/Models/Event.php +++ b/app/Models/Event.php @@ -0,0 +1,216 @@ +<?php + +require_once(__DIR__ . '/../../db/connect.php'); +require_once(__DIR__ . '/../util.php'); + +class EventModel { + + public function createEvent($event_name, $stock, $event_price, $event_date, $event_location, $gambar, $vid) { + global $db; + $success = true; + + if ($gambar !== null && isset($gambar['name']) && isset($gambar['tmp_name'])) { + $imageFilePath = '../assets/images/' . $gambar['name']; + + // Move the uploaded file to the desired directory + if (move_uploaded_file($gambar['tmp_name'], $imageFilePath)) { + echo "File uploaded successfully"; + } else { + echo "Error moving file to destination"; + $imageFilePath = null; // Set to null if there's an error + } + } else { + echo "No image uploaded"; + $imageFilePath = null; // Set to null if no file is uploaded + } + + if ($vid !== null && isset($vid['name']) && isset($vid['tmp_name'])) { + $vidFilePath = '../assets/videos/' . $vid['name']; + + // Move the uploaded file to the desired directory + if (move_uploaded_file($vid['tmp_name'], $vidFilePath)) { + echo "File uploaded successfully"; + } else { + echo "Error moving file to destination"; + $vidFilePath = null; // Set to null if there's an error + } + } else { + echo "No video uploaded"; + $vidFilePath = null; // Set to null if no file is uploaded + } + + if ($imageFilePath === null || $vidFilePath === null) { + $success = false; + return $success; + } + + $stmt = $db->prepare("INSERT INTO events (event_name, event_stock, event_price, event_date, event_location, gambar, vid) VALUES (?, ?, ?, ?, ?, ?, ?)"); + + if ($stmt->execute([$event_name, $stock, $event_price, $event_date, $event_location, $imageFilePath, $vidFilePath])) { + echo "Event created successfully"; + return $success; + } else { + echo "Error creating event: " . print_r($stmt->errorInfo(), true); + $success = false; + return $success; + } + } + + public function updateEvent($event_id, $event_name, $event_price, $event_date, $event_location, $gambar, $vid) { + global $db; + $db->setAttribute(PDO::ATTR_AUTOCOMMIT, 1); + + echo "Updating Event with ID: $event_id, Name: $event_name, Price: $event_price, Date: $event_date, Location: $event_location"; + + if ($gambar !== null && isset($gambar['name']) && isset($gambar['tmp_name'])) { + $imageFilePath = '../assets/images/' . $gambar['name']; + + // Move the uploaded file to the desired directory + if (move_uploaded_file($gambar['tmp_name'], $imageFilePath)) { + echo "File uploaded successfully<br>"; + } else { + echo "Error moving file to destination<br>"; + $imageFilePath = null; // Set to null if there's an error + } + } else { + echo "No image uploaded<br>"; + $imageFilePath = null; // Set to null if no file is uploaded + } + + if ($vid !== null && isset($vid['name']) && isset($vid['tmp_name'])) { + $vidFilePath = '../assets/videos/' . $vid['name']; + + // Move the uploaded file to the desired directory + if (move_uploaded_file($vid['tmp_name'], $vidFilePath)) { + echo "File uploaded successfully<br>"; + } else { + echo "Error moving file to destination<br>"; + $vidFilePath = null; // Set to null if there's an error + } + } else { + echo "No video uploaded<br>"; + $vidFilePath = null; // Set to null if no file is uploaded + } + + // chek wheter image or video is null or not + if ($imageFilePath === null && $vidFilePath === null) { + $sql = "UPDATE events SET event_name = '$event_name', event_price = '$event_price', event_date = '$event_date', event_location = '$event_location' WHERE event_id = $event_id"; + } elseif ($imageFilePath === null) { + $sql = "UPDATE events SET event_name = '$event_name', event_price = '$event_price', event_date = '$event_date', event_location = '$event_location', vid = '$vidFilePath' WHERE event_id = $event_id"; + } elseif ($vidFilePath === null) { + $sql = "UPDATE events SET event_name = '$event_name', event_price = '$event_price', event_date = '$event_date', event_location = '$event_location', gambar = '$imageFilePath' WHERE event_id = $event_id"; + } else { + $sql = "UPDATE events SET event_name = '$event_name', event_price = '$event_price', event_date = '$event_date', event_location = '$event_location', gambar = '$imageFilePath', vid = '$vidFilePath' WHERE event_id = $event_id"; + } + + $rowCount = $db->exec($sql); + + if ($rowCount !== false) { + echo "Event updated successfully. Rows affected: $rowCount"; + } else { + echo "Error updating event: " . print_r($db->errorInfo(), true); + } + return "Event updated successfully"; + } + + + + public function deleteEvent($eventId) { + global $db; + + try { + // Delete associated pembelian records + $stmtPembelian = $db->prepare("DELETE FROM pembelian WHERE ticket_id IN (SELECT ticket_id FROM tickets WHERE event_id = ?)"); + $stmtPembelian->execute([$eventId]); + + // Delete associated tickets + $stmtTickets = $db->prepare("DELETE FROM tickets WHERE event_id = ?"); + $stmtTickets->execute([$eventId]); + + // Delete the event + $stmtEvent = $db->prepare("DELETE FROM events WHERE event_id = ?"); + $stmtEvent->execute([$eventId]); + + echo "Event, associated tickets, and pembelian records deleted successfully<br>"; + + return "Event, associated tickets, and pembelian records deleted successfully"; + } catch (PDOException $e) { + echo "Error: " . $e->getMessage() . "<br>"; + + return "Error: " . $e->getMessage(); + } + } + + public function getAllEvents() { + global $db; + $stmt = $db->prepare("SELECT * FROM events"); + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + public function getEvent($eventId) { + global $db; + $stmt = $db->prepare("SELECT * FROM events WHERE event_id = ?"); + $stmt->execute([$eventId]); + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + public function getLastEventId() { + global $db; + $stmt = $db->prepare("SELECT event_id FROM events ORDER BY event_id DESC LIMIT 1"); + $stmt->execute(); + return $stmt->fetch(PDO::FETCH_ASSOC)['event_id']; + } + + public function searchEvents($searchQuery, $sortKey, $minStock) { + global $db; + + try { + $sql = "SELECT * FROM events WHERE 1"; // Start with a true condition + + // Add search condition + if (!empty($searchQuery)) { + $sql .= " AND (LOWER(event_name) LIKE CONCAT('%', :query, '%') OR LOWER(event_location) LIKE CONCAT('%', :query, '%'))"; + } + + + // Add filter condition + if ($minStock !== null) { + $sql .= " AND event_stock >= :minStock"; + } + + // Add sort condition + if ($sortKey === 'name') { + $sql .= " ORDER BY event_name ASC"; + } elseif ($sortKey === 'location') { + $sql .= " ORDER BY event_location ASC"; + } + + $stmt = $db->prepare($sql); + + // Bind search query parameter + if (!empty($searchQuery)) { + $lowerSearchQuery = strtolower("%" . $searchQuery . "%"); + $stmt->bindParam(':query', $lowerSearchQuery, PDO::PARAM_STR); + } + + // Bind filter parameter + if ($minStock !== null) { + $stmt->bindParam(':minStock', $minStock, PDO::PARAM_INT); + } + + $stmt->execute(); + + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } catch (PDOException $e) { + throw new Exception("Error searching events: " . $e->getMessage()); + } + } + + + + +} +?> + + diff --git a/app/Models/Pembelian.php b/app/Models/Pembelian.php index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0fcf741d91a4773be623aefe0029031038b12efb 100644 --- a/app/Models/Pembelian.php +++ b/app/Models/Pembelian.php @@ -0,0 +1,75 @@ +<?php +// app/models/Pembelian.php + +require_once(__DIR__ . '/../../db/connect.php'); +require_once(__DIR__ . '/../util.php'); + +class PembelianModel { + public function createPembelian($ticketId, $userId, $createdTime) { + global $db; + + $stmt = $db->prepare("INSERT INTO pembelian (ticket_id, user_id, pembelian_created_time) VALUES (?, ?, ?)"); + if ($stmt->execute([$ticketId, $userId, $createdTime])) { + $stmt = $db->prepare("SELECT event_id FROM tickets WHERE ticket_id = ?"); + $stmt->execute([$ticketId]); + $result = $stmt->fetch(PDO::FETCH_ASSOC); + + if ($result) { + $eventId = $result['event_id']; + + // Decrement the event's stock by 1 + $stmt = $db->prepare("UPDATE events SET event_stock = event_stock - 1 WHERE event_id = ?"); + return $stmt->execute([$eventId]); + } + } + } + + public function getPembelian($pembelianId) { + global $db; + + $stmt = $db->prepare("SELECT * FROM pembelian WHERE pembelian_id = ?"); + $stmt->execute([$pembelianId]); + + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + public function updatePembelian($pembelianId, $ticketId, $userId, $createdTime) { + global $db; + + $stmt = $db->prepare("UPDATE pembelian SET ticket_id = ?, user_id = ?, pembelian_created_time = ? WHERE pembelian_id = ?"); + $stmt->execute([$ticketId, $userId, $createdTime, $pembelianId]); + + return "Pembelian updated successfully"; + } + + public function deletePembelian($pembelianId) { + global $db; + + $stmt = $db->prepare("DELETE FROM pembelian WHERE pembelian_id = ?"); + $stmt->execute([$pembelianId]); + + return "Pembelian deleted successfully"; + } + + public function getAllPembelian(){ + global $db; + $stmt = $db->prepare("SELECT * FROM pembelian"); + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + public function getAllPembelianWithDetails(){ + global $db; + $stmt = $db->prepare("SELECT * FROM pembelian JOIN tickets ON pembelian.ticket_id = tickets.ticket_id JOIN events ON tickets.event_id = events.event_id ORDER BY pembelian_created_time DESC"); + $stmt->execute(); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + public function getPembelianByUserId($userId){ + global $db; + $stmt = $db->prepare("SELECT * FROM pembelian JOIN tickets ON pembelian.ticket_id = tickets.ticket_id JOIN events ON tickets.event_id = events.event_id WHERE user_id = ? ORDER BY pembelian_created_time DESC"); + $stmt->execute([$userId]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } +} +?> diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4060c6ede2da98c4e084c8bef084f05fe8651aa4 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -0,0 +1,76 @@ +<?php +// app/models/Ticket.php + +require_once(__DIR__ . '/../../db/connect.php'); + + +class TicketModel { + public function createTicket($name, $eventId) { + global $db; + + $stmt = $db->prepare("INSERT INTO tickets (ticket_name, event_id) VALUES (?, ?)"); + $stmt->execute([$name, $eventId]); + + return "Ticket created successfully"; + } + + public function getTicket($ticketId) { + global $db; + + $stmt = $db->prepare("SELECT * FROM tickets WHERE ticket_id = ?"); + $stmt->execute([$ticketId]); + + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + public function updateTicket($ticketId, $name, $eventId) { + global $db; + + $stmt = $db->prepare("UPDATE tickets SET ticket_name = ?, event_id = ? WHERE ticket_id = ?"); + $stmt->execute([$name, $eventId, $ticketId]); + + return "Ticket updated successfully"; + } + + public function updateTicketByEventId($eventId, $name) { + global $db; + + $stmt = $db->prepare("UPDATE tickets SET ticket_name = ? WHERE event_id = ?"); + $stmt->execute([$name, $eventId]); + + return "Ticket updated successfully"; + } + + public function deleteTicket($ticketId) { + global $db; + + $stmt = $db->prepare("DELETE FROM tickets WHERE ticket_id = ?"); + $stmt->execute([$ticketId]); + + return "Ticket deleted successfully"; + } + + public function getSmallestAvailableTicket($eventId) { + global $db; + + $stmt = $db->prepare(" + SELECT t.*, e.* + FROM tickets t + JOIN events e ON t.event_id = e.event_id + WHERE t.event_id = :eventId + AND NOT EXISTS ( + SELECT 1 + FROM pembelian p + WHERE p.ticket_id = t.ticket_id + ) + ORDER BY t.ticket_id ASC + LIMIT 1 + "); + + $stmt->bindParam(':eventId', $eventId, PDO::PARAM_INT); + $stmt->execute(); + + return $stmt->fetch(PDO::FETCH_ASSOC); + } +} +?> diff --git a/app/Models/User.php b/app/Models/User.php index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cb3410b23a9c471bcbdda9d7cdec126657941759 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -0,0 +1,109 @@ +<?php +// app/models/User.php + +require_once(__DIR__ . '/../../db/connect.php'); + + +class UserModel { + public function createUser($name, $username, $email, $hashedPass, $isAdmin) { + global $db; + + $response = [ + 'success'=> true, + 'message'=> '' + ]; + + try { + $stmt = $db->prepare("INSERT INTO users (user_name, username, user_email, user_hashedPass, isAdmin) VALUES (?, ?, ?, ?, ?)"); + $stmt->execute([$name, $username, $email, $hashedPass, $isAdmin]); + $response['message'] = "User created successfully"; + return $response; + } catch (PDOException $e) { + $response['success'] = false; + // Check if the error code corresponds to a unique constraint violation. + if ($e->getCode() === '23000') { + // Handle the error as a duplicate entry. + $response['message'] = "Username or email already exists. Please choose a different one."; + return $response; + } else { + // Handle other database errors. + $response['message'] = "Database error: " . $e->getMessage(); + return $response; + } + } + } + + + // User.php + public function getUser($userId) { + global $db; + + $stmt = $db->prepare("SELECT * FROM users WHERE user_ID = ?"); + $stmt->execute([$userId]); + + if ($stmt) { + $userData = $stmt->fetch(PDO::FETCH_ASSOC); + + // Check if user data is found + if ($userData) { + return $userData; + } else { + return null; + } + } else { + return null; + } + } + + public function getUserByEmail($email) { + global $db; + + $stmt = $db->prepare("SELECT * FROM users WHERE user_email = ?"); + $stmt->execute([$email]); + + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + public function getUserByUsername($username){ + global $db; + + $stmt = $db->prepare("SELECT * FROM users WHERE username = ?"); + $stmt->execute([$username]); + + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + public function updateUser($userId, $name, $username, $email, $hashedPass, $isAdmin) { + global $db; + + $stmt = $db->prepare("UPDATE users SET user_name = ?, username = ?, user_email = ?, user_hashedPass = ?, isAdmin = ? WHERE user_ID = ?"); + $stmt->execute([$name, $username, $email, $hashedPass, $isAdmin, $userId]); + + return "User updated successfully"; + } + + public function editProfile($userId, $name, $username, $email) { + global $db; + + $stmt = $db->prepare("UPDATE users SET user_name = ?, username = ?, user_email = ? WHERE user_id = ?"); + return $stmt->execute([$name, $username, $email, $userId]); + } + + public function deleteUser($userId) { + global $db; + + $stmt = $db->prepare("DELETE FROM users WHERE user_ID = ?"); + $stmt->execute([$userId]); + + return "User deleted successfully"; + } + + public function getAllUsers() { + global $db; + + $stmt = $db->query("SELECT * FROM users"); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + +} +?> diff --git a/app/Views/event/create.php b/app/Views/event/create.php new file mode 100644 index 0000000000000000000000000000000000000000..476cc49acfd78c636c3375dea324a3eb342b412c --- /dev/null +++ b/app/Views/event/create.php @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<html lang="en"> + + <head> + <title>Create an Event</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/../../styles/global.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/form.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/footer.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/navbar.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/container.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/auth.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/dropdown.css"> + </head> + + <body> + <?php include '../template/navbar.php';?> + <!-- <h1 id="title">Create an Event</h1> --> + <div class="main-content"> + <form method="post" class="event-form" action="../../router.php" enctype="multipart/form-data"> + <input type="text" name="eventName" placeholder="Name" required> + <input type="number" name="eventStock" placeholder="Stock" required> + <input type="number" name="eventPrice" placeholder="Price" required> + <input type="datetime-local" name="eventDate" required> + <input type="text" name="eventLocation" placeholder="Location" required> + + <!-- Drag-and-drop area for image upload --> + <div id="drop-area-create"> + <p>Drag and drop an image file here or click to select one.</p> + <input type="file" id="file-input-create" name="gambar" accept="image/*" required> + </div> + + <div id="drop-area-create"> + <p>Drag and drop an video file here or click to select one.</p> + <input type="file" id="file-input-create" name="vid" accept="video/*" required> + </div> + <div id="button-container"> + <button type="submit" name="eventAction" value="createEvent">Create</button> + <button type="button" id="cancelButton">Cancel</button> + </div> + </form> + </div> + <?php include '../template/footer.php';?> + + <script> + const cancelButton = document.getElementById("cancelButton"); + cancelButton.addEventListener("click", function() { + window.location.href = "/app/Views/home/home.php"; + }); + </script> + </body> +</html> \ No newline at end of file diff --git a/app/Views/event/update.php b/app/Views/event/update.php new file mode 100644 index 0000000000000000000000000000000000000000..60eaff84d79cbae435db05388900d99a4643acdf --- /dev/null +++ b/app/Views/event/update.php @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<html lang="en"> + + <head> + <title>Update Event</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/../../styles/global.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/form.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/footer.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/navbar.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/container.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/auth.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/dropdown.css"> + </head> + + <body> + <?php include '../template/navbar.php';?> + <!-- <h1>Update Event</h1> --> + <div class="main-content"> + <form method="post" class="event-form" action="../../router.php" enctype="multipart/form-data"> + <input type="hidden" name="eventId" value="<?php echo $_GET['id']; ?>"> + <input type="text" name="eventName" placeholder="Name" required> + <input type="number" name="eventPrice" placeholder="Price" required> + <input type="datetime-local" name="eventDate" required> + <input type="text" name="eventLocation" placeholder="Location" required> + + <!-- Drag-and-drop area for image upload --> + <div id="drop-area-create"> + <p>Drag and drop an image file here or click to select one.</p> + <input type="file" id="file-input-create" name="gambar" accept="image/*"> + </div> + + <!-- <input type="text" name="vid" placeholder="Video URL"> --> + <div id="drop-area-create"> + <p>Drag and drop an video file here or click to select one.</p> + <input type="file" id="file-input-create" name="vid" accept="video/*"> + </div> + <div id="button-container"> + <button type="submit" name="eventAction" value="updateEvent">Update</button> + <button type="button" id="cancelButton">Cancel</button> + </div> + </form> + </div> + <?php include '../template/footer.php';?> + + <script> + // get event id from get request + const urlParams = new URLSearchParams(window.location.search); + const eventId = urlParams.get('id'); + + document.addEventListener("DOMContentLoaded", function(){ + var xhr = new XMLHttpRequest(); + + xhr.open('GET', '../../router.php?eventAction=getEvent&eventId=' + eventId, true); + + // Set up a function to run when the request is successfully completed + xhr.onload = function () { + if (xhr.status >= 200 && xhr.status < 300) { + // Parse the JSON + var data = JSON.parse(xhr.responseText); + + // Log and utilize data + console.log(data); + document.querySelector('input[name="eventName"]').value = data.event_name; + document.querySelector('input[name="eventPrice"]').value = data.event_price; + document.querySelector('input[name="eventDate"]').value = data.event_date; + document.querySelector('input[name="eventLocation"]').value = data.event_location; + } else { + // Handle error: log status text + console.log('Failed to fetch event:', xhr.statusText); + } + }; + + // Set up a function to handle any errors that may occur + xhr.onerror = function () { + console.error('Request failed:', xhr.statusText); + }; + + // Send the request + xhr.send(); + }); + + const cancelButton = document.getElementById("cancelButton"); + cancelButton.addEventListener("click", function() { + window.location.href = "/app/Views/home/home.php"; + }); + </script> + </body> +</html> \ No newline at end of file diff --git a/app/Views/history/history.php b/app/Views/history/history.php new file mode 100644 index 0000000000000000000000000000000000000000..810779df9c5beccde0873d6ad8260662e014465f --- /dev/null +++ b/app/Views/history/history.php @@ -0,0 +1,90 @@ +<?php + session_start(); + ob_start(); + if (!isset($_SESSION["user_id"])) { + echo "here"; + // User is not authenticated; redirect to login page + header("Location: /app/Views/login/login.php"); + ob_end_flush(); + } + + require_once '../../Controllers/PembelianController.php'; + require_once '../../Controllers/TicketController.php'; + require_once '../../Controllers/UserController.php'; + require_once '../../Controllers/EventController.php'; + + + $pembelianController = new PembelianController(); + $userController = new UserController(); + + $isAdmin = $userController->getUser($_SESSION['user_id'])['isAdmin']; + + // Get search results or all pembelian (history) + if (!$isAdmin){ + $pembelians = $pembelianController->getPembelianByUserId($_SESSION['user_id']); + } else { + $pembelians = $pembelianController->getAllPembelianWithDetails(); + } + $paginationData = $pembelianController->paginateHistory($pembelians); + $pembelians = $paginationData['pembelian']; + $totalEvents = $paginationData['total']; + $currentPage = $paginationData['page']; + $maxPage = $paginationData['maxpage']; +?> + +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>History</title> + <link rel="stylesheet" type="text/css" href="/../../styles/global.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/form.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/footer.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/navbar.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/container.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/auth.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/dropdown.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/pagination.css"> +</head> + +<body> +<!-- Navbar --> + <?php include '../template/navbar.php';?> + + <div class="main-content"> + <div class="column"> + <h2>All History</h2> + <?php foreach ($pembelians as $pembelian) : + include '../template/history.php'; + endforeach;?> + </div> + <div class="pagination"> + <?php if ($currentPage > 1) : ?> + <a href="?page=<?= $currentPage - 1 ?>">Previous</a> + <?php endif; ?> + + <?php for ($i = 1; $i <= $maxPage; $i++) : ?> + <a href="?page=<?= $i ?>" <?= ($i == $currentPage) ? 'class="active"' : '' ?>><?= $i ?></a> + <?php endfor; ?> + + <?php if ($currentPage < $maxPage) : ?> + <a href="?page=<?= $currentPage + 1 ?>">Next</a> + <?php endif; ?> + </div> + + </div> + + <?php include '../template/footer.php';?> + + <script defer> + function toggleMenu() { + const navbar = document.querySelector('.navbar ul'); + navbar.classList.toggle('show'); + } + </script> + <script src="js/script.js"></script> +</body> + +</html> \ No newline at end of file diff --git a/app/Views/home/home.php b/app/Views/home/home.php new file mode 100644 index 0000000000000000000000000000000000000000..b0871af0d1c7a97853a3f1790cd1beee7398f735 --- /dev/null +++ b/app/Views/home/home.php @@ -0,0 +1,169 @@ +<?php + session_start(); + ob_start(); + if (!isset($_SESSION["user_id"])) { + echo "here"; + // User is not authenticated; redirect to login page + header("Location: /app/Views/login/login.php"); + ob_end_flush(); + } + + require_once '../../Controllers/PembelianController.php'; + require_once '../../Controllers/TicketController.php'; + require_once '../../Controllers/UserController.php'; + require_once '../../Controllers/EventController.php'; + + $eventController = new EventController(); + $pembelianController = new PembelianController(); + $ticketController = new TicketController(); + $userController = new UserController(); + + // Handle search query + $searchQuery = isset($_GET['search']) ? $_GET['search'] : ''; + $sortKey = isset($_GET['sort']) ? $_GET['sort'] : 'name'; + $minStock = isset($_GET['min_stock']) ? $_GET['min_stock'] : null; + + // Get search results or all events + $searchResults = $eventController->searchEvents($searchQuery, $sortKey, $minStock); + if (!isset($_GET['page'])) { + $_GET['page'] = 1; + } + $paginationData = $eventController->paginateEvents($searchResults,$_GET['page']); + $events = $paginationData['events']; + $totalEvents = $paginationData['total']; + $currentPage = $paginationData['page']; + $maxPage = $paginationData['maxpage']; + + $isAdmin = $userController->getUser($_SESSION['user_id'])['isAdmin']; +?> + +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>HOME • TICKET KU</title> + <link rel="stylesheet" type="text/css" href="/../../styles/global.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/form.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/footer.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/navbar.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/container.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/auth.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/dropdown.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/pagination.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/media.css"> +</head> + +<body> +<!-- Navbar --> + <?php include '../template/navbar.php';?> + + <div class="main-content"> + <div class="column"> + <?php if ($isAdmin) : ?> + <button onclick="openCreateEventPage()" id="createEventBtn" class="admin">Create Event</button> + <?php endif; ?> + <div class="row"> + <input type="text" id="searchInput" placeholder="Search.." value="<?= htmlspecialchars($searchQuery)?>" style="width:250%;"> + <select name="sort" id="sortSelect" class="styled-select"> + <option value="" <?= empty($sortKey) ? 'selected' : '' ?>>No Sorting</option> + <option value="name" <?= $sortKey === 'name' ? 'selected' : '' ?>>Sort by Name</option> + <option value="location" <?= $sortKey === 'location' ? 'selected' : '' ?>>Sort by Location</option> + </select> + <!-- Change input type to "text" for minimum stock --> + <input type="text" id="minStockInput" name="min_stock" placeholder="Min Stock" value="<?= htmlspecialchars($minStock) ?>"> + <button type="submit" id="search-button">Search</button> + </div> + + <?php foreach ($events as $event) : + include '../template/event.php'; + endforeach;?> + + <div class="pagination"> + <?php if ($currentPage > 1) : ?> + <a href="?page=<?= $currentPage - 1 ?>">Previous</a> + <?php endif; ?> + + <?php for ($i = 1; $i <= $maxPage; $i++) : ?> + <a href="?page=<?= $i ?>" <?= ($i == $currentPage) ? 'class="active"' : '' ?>><?= $i ?></a> + <?php endfor; ?> + + <?php if ($currentPage < $maxPage) : ?> + <a href="?page=<?= $currentPage + 1 ?>">Next</a> + <?php endif; ?> + </div> + </div> + </div> + + <?php include '../template/footer.php';?> + + <script defer> + function toggleMenu() { + const navbar = document.querySelector('.navbar ul'); + navbar.classList.toggle('show'); + } + + function openCreateEventPage() { + window.location.href = "/app/Views/event/create.php"; + } + + let debounceTimer; + let searchInput = document.getElementById('searchInput'); + let cursorPosition = 0; + + // Initialize the cursor position + searchInput.addEventListener('input', function () { + cursorPosition = this.selectionStart; + }); + + function debounceSearch() { + clearTimeout(debounceTimer); + debounceTimer = setTimeout(function () { + // Get the search input values + const searchQuery = searchInput.value; + const minStock = document.getElementById('minStockInput').value; + const sortKey = document.getElementById('sortSelect').value; + + // Construct the URL with search, min_stock, and sort parameters + const url = `/app/Views/home/home.php?search=${encodeURIComponent(searchQuery)}&min_stock=${encodeURIComponent(minStock)}&sort=${encodeURIComponent(sortKey)}`; + + // Redirect to the updated URL + window.location.href = url; + + // Restore focus and cursor position + searchInput.focus(); + searchInput.setSelectionRange(cursorPosition, cursorPosition); + }, 500); // Adjust the debounce delay as needed (in milliseconds) + } + + // Attach the debounce function to the search input's input event + searchInput.addEventListener('input', debounceSearch); + document.getElementById('minStockInput').addEventListener('input', debounceSearch); + // Attach an event listener to the sort dropdown + document.getElementById('sortSelect').addEventListener('change', function () { + // Trigger the debounceSearch function when the sorting option changes + debounceSearch(); + }); + + // Function to show delete confirmation dialog + function showConfirmationDialog(eventId) { + if (confirm("Are you sure you want to delete this event?")) { + // If the user confirms, submit the corresponding form + document.getElementById("deleteEventForm" + eventId).submit(); + } + } + + // Attach the confirmation dialog to all buttons with the class "confirmDeleteBtn" + const deleteButtons = document.querySelectorAll(".confirmDeleteBtn"); + deleteButtons.forEach(button => { + button.addEventListener("click", function() { + const eventId = this.getAttribute("data-eventid"); + showConfirmationDialog(eventId); + }); + }); + </script> + +</body> + +</html> diff --git a/app/Views/login/login.php b/app/Views/login/login.php new file mode 100644 index 0000000000000000000000000000000000000000..2ca035a560f262411451a7f910311c690fa21fdb --- /dev/null +++ b/app/Views/login/login.php @@ -0,0 +1,37 @@ +<?php + session_start(); +?> +<!DOCTYPE html> +<html lang="en"> + + <head> + <title>Login • TICKET KU</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="../../../styles/auth.css"> + </head> + + <body> + <div class="login-container"> + <h1>Login</h1> + <form id="loginForm" method="post" action="../../router.php"> + <!-- Aslinya email atau username bisa(?) --> + <label for="email">Email or Username</label> + <input type="text" id="identifier" name="loginIdentifier" required> + + <label for="password">Password</label> + <input type="password" id="password" name="loginPassword" required> + + <?php + // Check if there is an error message in the session + if (isset($_SESSION['message'])) { + echo '<p>' . $_SESSION['message'] . '</p>'; + unset($_SESSION['message']); // Remove the error message from the session + } + ?> + <button type="submit" name="userAction" value="login">Login</button> + </form> + <p>Don't have an account? <a href="register.php">Register</a></p> + </div> + </body> +</html> \ No newline at end of file diff --git a/app/Views/login/register.php b/app/Views/login/register.php new file mode 100644 index 0000000000000000000000000000000000000000..bdd3a0966d3145b309d50affbab88467c515ea29 --- /dev/null +++ b/app/Views/login/register.php @@ -0,0 +1,47 @@ +<?php + session_start(); +?> +<!DOCTYPE html> +<html lang="en"> + + <head> + <title>Register • TICKET KU</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="../../../styles/auth.css"> + </head> + + <body> + <div class="register-container"> + <h1>Register</h1> + <form id="registerForm" method="post" action="../../router.php"> + <label for="userName">Name</label> + <input type="text" id="userName" name="userName" required> + + <label for="userUsername">Username</label> + <input type="text" id="userUsername" name="userUsername" required> + + <label for="userEmail">Email</label> + <input type="email" id="userEmail" name="userEmail" required> + + <label for="userPassword">Password</label> + <input type="password" id="userPassword" name="userPassword" required> + + <label for="isAdmin" class="checkbox-label"> + Admin <input type="checkbox" id="isAdmin" name="isAdmin" value="1"> + </label> + + <?php + // Check if there is an error message in the session + if (isset($_SESSION['error_message'])) { + echo '<p>' . $_SESSION['error_message'] . '</p>'; + unset($_SESSION['error_message']); // Remove the error message from the session + } + ?> + + <button type="submit" name="userAction" value="createUser">Register</button> + </form> + <p>Already have an account? <a href="login.php">Login</a></p> + </div> + </body> +</html> \ No newline at end of file diff --git a/app/Views/pembelian/pembelian.php b/app/Views/pembelian/pembelian.php new file mode 100644 index 0000000000000000000000000000000000000000..00d8ce4560b65c3e8d7c566cda67c1ad52ecbfbc --- /dev/null +++ b/app/Views/pembelian/pembelian.php @@ -0,0 +1,86 @@ +<?php + session_start(); + ob_start(); + if (!isset($_SESSION["user_id"])) { + echo "here"; + // User is not authenticated; redirect to login page + header("Location: /app/Views/login/login.php"); + ob_end_flush(); + } +?> + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Purchase Ticket</title> + <link rel="stylesheet" type="text/css" href="/../../styles/global.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/form.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/footer.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/navbar.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/container.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/auth.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/media.css"> + <!-- Include any CSS styles or external libraries here --> +</head> +<body> + + <?php include 'Views/template/navbar.php';?> + + <div class="main-content"> + <div class="column"> + <h1>Purchase Ticket</h1> + <div class="event-container"> + <h2><?php echo $ticket['event_name']; ?></h2> + <div class="row"> + <img src="<?php echo '../../../'.$ticket['gambar']; ?>" alt="<?php echo $ticket['event_name']; ?>" width="35%"> + <video width="60%" controls> + <source src="<?php echo '../../../'.$ticket['vid']; ?>" type="video/mp4"> + Your browser does not support the video tag. + </video> + </div> + <div class="row"> + <div class="column" width="250"> + <p><strong>Location:</strong> <?php echo $ticket['event_location']; ?></p> + <p><strong>Date/Time:</strong> <?php echo $ticket['event_date']; ?></p> + <p><strong>Ticket Price:</strong> <?php echo $ticket['event_price']; ?></p> + <p><strong>Tickets Left:</strong> <?php echo $ticket['event_stock']; ?></p> + </div> + <!-- Display the ticket details --> + <div class="column"> + <h2>Ticket Details</h2> + <p>Ticket ID: <?php echo $ticket['ticket_id']; ?></p> + <p>Ticket Name: <?php echo $ticket['ticket_name']; ?></p> + + <!-- Purchase Form --> + </div> + <form id="purchaseForm" method="post" action="/app/router.php"> + <input type="hidden" name="ticketId" value="<?php echo $ticket['ticket_id']; ?>"> + <input type="hidden" name="userId" value="<?php echo $_SESSION['user_id']; ?>"> + <input type="hidden" name="purchaseAction" value="purchaseTicket"> + <button type="button" id="confirmPurchaseBtn">Confirm Purchase</button> + </form> + </div> + </div> + </div> + </div> + <!-- JavaScript code for the confirmation dialog --> + <script> + // Function to show the confirmation dialog + function showConfirmationDialog() { + if (confirm("Are you sure you want to purchase this ticket?")) { + // If the user confirms, submit the form + document.getElementById("purchaseForm").submit(); + } + } + + // Attach the confirmation dialog to the button click event + document.getElementById("confirmPurchaseBtn").addEventListener("click", showConfirmationDialog); + </script> + + <!-- Include any additional content or messages here --> + <?php include 'Views/template/footer.php';?> + +</body> +</html> diff --git a/app/Views/profile/edit_profile.php b/app/Views/profile/edit_profile.php new file mode 100644 index 0000000000000000000000000000000000000000..da5e7c5b527cbb4b36b342d21f321fb7c80ea8a5 --- /dev/null +++ b/app/Views/profile/edit_profile.php @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <title>Update Event</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/../../styles/global.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/form.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/footer.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/navbar.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/container.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/auth.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/dropdown.css"> + </head> + + <body> + <?php include '../template/navbar.php';?> + <div class="main-content"> + <form method="post" class="event-form" action="../../router.php" enctype="multipart/form-data"> + <input type="hidden" name="userId" value="<?php echo $_SESSION['user_id']; ?>"> + <input type="text" name="user_name" placeholder="Name" required> + <input type="text" name="user_username" placeholder="Username" required> + <input type="email" name="user_email" placeholder="Email" required> + <div id="button-container"> + <button type="submit" name="userAction" value="editProfile">Save Changes</button> + <button type="button" id="cancelButton">Cancel</button> + </div> + </form> + </div> + <?php include '../template/footer.php'; ?> + <script> + document.addEventListener("DOMContentLoaded", function(){ + var xhr = new XMLHttpRequest(); + + xhr.open('GET', '../../router.php?userAction=getUser', true); + + // Set up a function to run when the request is successfully completed + xhr.onload = function () { + if (xhr.status >= 200 && xhr.status < 300) { + // Parse the JSON + var data = JSON.parse(xhr.responseText); + + // Log and utilize data + document.querySelector('input[name="user_name"]').value = data.user_name; + document.querySelector('input[name="user_username"]').value = data.username; + document.querySelector('input[name="user_email"]').value = data.user_email; + } else { + // Handle error: log status text + console.log('Failed to fetch user:', xhr.statusText); + } + }; + + // Set up a function to handle any errors that may occur + xhr.onerror = function () { + console.error('Request failed:', xhr.statusText); + }; + + // Send the request + xhr.send(); + }); + + const cancelButton = document.getElementById("cancelButton"); + cancelButton.addEventListener("click", function() { + window.location.href = "/app/Views/profile/view_profile.php"; + }); + </script> + </body> +</html> diff --git a/app/Views/profile/view_profile.php b/app/Views/profile/view_profile.php new file mode 100644 index 0000000000000000000000000000000000000000..2eab3bcced344f665141e7c4879a072fa6607f73 --- /dev/null +++ b/app/Views/profile/view_profile.php @@ -0,0 +1,88 @@ +<?php + session_start(); + ob_start(); + if (!isset($_SESSION["user_id"])) { + echo "here"; + } + + $userId = $_SESSION['user_id'] ?? null; + $userData = null; + + if ($userId !== null) { + require_once '../../Controllers/UserController.php'; + $userController = new UserController(); + + $userData = $userController->getUser($userId); + } +?> + +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <title>View Profile • TICKET KU</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" type="text/css" href="/../../styles/global.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/navbar.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/container.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/auth.css"> + <link rel="stylesheet" type="text/css" href="/../../styles/footer.css"> + <!-- Add other stylesheets as needed --> +</head> + +<body> + <?php include '../template/navbar.php';?> + <div class="main-content"> + <div class="profile-container"> + <h1>View Profile</h1> + <form id="viewProfileForm" action="edit_profile.php"> + <?php + if ($userData !== null) { + // Display user data if the keys exist + if (isset($userData['user_ID'])) { + echo "<p><strong>User ID:</strong> {$userData['user_ID']}</p>"; + } else { + echo "User ID not available."; + } + + if (isset($userData['username'])) { + echo "<p><strong>Username:</strong> {$userData['username']}</p>"; + } else { + echo "Username not available."; + } + + if (isset($userData['user_name'])) { + echo "<p><strong>Username:</strong> {$userData['user_name']}</p>"; + } else { + echo "Username not available."; + } + + if (isset($userData['user_email'])) { + echo "<p><strong>Email:</strong> {$userData['user_email']}</p>"; + } else { + echo "Email not available."; + } + } else { + echo "User data not available."; + } + ?> + + <form method="get" action="edit_profile.php"> + <input type="hidden" name="userAction" value="viewProfile"> + <button type="submit">Edit Profile</button> + </form> + </form> + </div> + </div> + <?php include '../template/footer.php';?> + <script defer> + function toggleMenu() { + const navbar = document.querySelector('.navbar ul'); + navbar.classList.toggle('show'); + } + </script> + <script src="js/script.js"></script> +</body> + +</html> diff --git a/app/Views/template/event.php b/app/Views/template/event.php new file mode 100644 index 0000000000000000000000000000000000000000..066234778da4df577d4be60894e3c528238eb966 --- /dev/null +++ b/app/Views/template/event.php @@ -0,0 +1,31 @@ +<div class="event-container"> + <h2><?php echo $event['event_name']; ?></h2> + <div class="row"> + <img src="<?php echo '../../../'.$event['gambar']; ?>" alt="<?php echo $event['event_name']; ?>" width="200"> + <div class="column"> + <p><strong>Location:</strong> <?php echo $event['event_location']; ?></p> + <p><strong>Date/Time:</strong> <?php echo $event['event_date']; ?></p> + <p><strong>Ticket Price:</strong> <?php echo $event['event_price']; ?></p> + <p><strong>Tickets Left:</strong> <?php echo $event['event_stock']; ?></p> + </div> + <form id="viewEventForm" method="get" action="/app/router.php"> + <input type="hidden" name="eventId" value="<?php echo $event['event_id']; ?>"> + <?php if (!$isAdmin) : ?> + <button type="submit" name="ticketAction" value="getTicket">Buy Ticket</button> + <?php endif; ?> + </form> + <?php if ($isAdmin) : ?> + <div class="colummn"> + <form id="viewEventForm" method="get" action="../event/update.php"> + <input type="hidden" name="id" value="<?php echo $event['event_id']; ?>"> + <button type="submit" id="updateEvent">Update Event</button> + </form> + <form id="deleteEventForm<?php echo $event['event_id']; ?>" method="post" action="/app/router.php"> + <input type="hidden" name="eventId" value="<?php echo $event['event_id']; ?>"> + <input type="hidden" name="eventAction" value="deleteEvent"> + <button type="button" class="confirmDeleteBtn" data-eventid="<?php echo $event['event_id']; ?>">Delete Event</button> + </form> + </div> + <?php endif; ?> + </div> +</div> \ No newline at end of file diff --git a/app/Views/template/footer.php b/app/Views/template/footer.php new file mode 100644 index 0000000000000000000000000000000000000000..74080d285a7ff41e22cd7689be1e8ff650f36d98 --- /dev/null +++ b/app/Views/template/footer.php @@ -0,0 +1,3 @@ +<div class="footer"> + © <?php echo date("Y"); ?> Ticket Ku. All rights reserved. +</div> \ No newline at end of file diff --git a/app/Views/template/history.php b/app/Views/template/history.php new file mode 100644 index 0000000000000000000000000000000000000000..dbc33617b8f98ce4e0a32179079bcdd910cc6849 --- /dev/null +++ b/app/Views/template/history.php @@ -0,0 +1,15 @@ +<div class="history-container"> + <!-- <h2><?php echo $pembelian['pembelian_id']; ?></h2> --> + <div class="row"><img src="<?php echo '../../../'.$pembelian['gambar']; ?>" alt="<?php echo $pembelian['event_name']; ?>" width="30%"> + <div class="column"> + <p><strong>Location:</strong> <?php echo $pembelian['event_location']; ?></p> + <p><strong>Date/Time:</strong> <?php echo $pembelian['event_date']; ?></p> + <p><strong>Ticket Price:</strong> <?php echo $pembelian['event_price']; ?></p> + </div> + <div class="column"> + <p><strong>Ticket ID:</strong> <?php echo $pembelian['ticket_id']; ?></p> + <p><strong>User ID:</strong> <?php echo $pembelian['user_id']; ?></p> + <p><strong>Purchase Time:</strong> <?php echo $pembelian['pembelian_created_time']; ?></p> + </div> + </div> +</div> \ No newline at end of file diff --git a/app/Views/template/navbar.php b/app/Views/template/navbar.php new file mode 100644 index 0000000000000000000000000000000000000000..ec8dd2abb5c9159fc789683ffbc72588471a9771 --- /dev/null +++ b/app/Views/template/navbar.php @@ -0,0 +1,16 @@ +<div class="navbar"> + <div class="row"> + <div class="navbar-toggle" onclick="toggleMenu()">☰</div> + <ul> + <li><a href="/app/Views/home/home.php">Home</a></li> + <li><a href="/app/Views/profile/view_profile.php">Profile</a></li> + <li><a href="/app/Views/history/history.php">History</a></li> + <!-- Logout Button --> + <li> + <form method="post" action="/app/router.php"> + <button type="submit" name="userAction" value="logout">Logout</button> + </form> + </li> + </ul> + </div> +</div> \ No newline at end of file diff --git a/app/router.php b/app/router.php new file mode 100644 index 0000000000000000000000000000000000000000..7156e5c4dea8e7de894027f76c3b7fb0b37c0ccd --- /dev/null +++ b/app/router.php @@ -0,0 +1,33 @@ +<?php + +require_once './Controllers/PembelianController.php'; +require_once './Controllers/TicketController.php'; +require_once './Controllers/UserController.php'; +require_once './Controllers/EventController.php'; + +$eventController = new EventController(); +$pembelianController = new PembelianController(); +$ticketController = new TicketController(); +$userController = new UserController(); + +if ($_SERVER["REQUEST_METHOD"] == "POST") { + if (isset($_POST["eventAction"])) { + $eventController->handleRequest(); + } elseif (isset($_POST["ticketAction"])) { + $ticketController->handleRequest(); + } elseif (isset($_POST["userAction"])) { + $userController->handleRequest(); + } elseif (isset($_POST["purchaseAction"])) { + $pembelianController->handleRequest(); + } + +} elseif ($_SERVER["REQUEST_METHOD"] == "GET") { + if (isset($_GET["eventAction"]) && isset($_GET["eventId"])) { + $eventController->handleRequest(); + } elseif (isset($_GET["ticketAction"])) { + $ticketController->handleRequest(); + } elseif (isset($_GET["userAction"])) { + $userController->handleRequest(); + } +} +?> \ No newline at end of file diff --git a/app/util.php b/app/util.php new file mode 100644 index 0000000000000000000000000000000000000000..da3c6d57c9624c8dcc4ed19faa9297280b76715d --- /dev/null +++ b/app/util.php @@ -0,0 +1,13 @@ +<?php +function preprocess($str) { + return str_replace(' ', '%20', $str); +} + +function formatPrice($price) { + return number_format($price,0,',','.'); +} + +function isEmailValid($email) { + return filter_var($email, FILTER_VALIDATE_EMAIL); +} +?> \ No newline at end of file diff --git a/assets/images/1.jpg b/assets/images/1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0f98933a4c89043b90b74ed8f693db9042357542 Binary files /dev/null and b/assets/images/1.jpg differ diff --git a/assets/images/10.jpg b/assets/images/10.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e6188c8e11294c4aade8103916c419c5dd111104 Binary files /dev/null and b/assets/images/10.jpg differ diff --git a/assets/images/11.png b/assets/images/11.png new file mode 100644 index 0000000000000000000000000000000000000000..a95c47f09e98bbfe02ed1653d52182981dfbf644 Binary files /dev/null and b/assets/images/11.png differ diff --git a/assets/images/12.jpg b/assets/images/12.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b2803f56ca06409ba9c2bba9aefbc97440f19820 Binary files /dev/null and b/assets/images/12.jpg differ diff --git a/assets/images/13.jpg b/assets/images/13.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d639dc5d8231496cdb787d77a06d3ca73070b223 Binary files /dev/null and b/assets/images/13.jpg differ diff --git a/assets/images/14.jpg b/assets/images/14.jpg new file mode 100644 index 0000000000000000000000000000000000000000..482d989ad8f883f046aa24e4ade3e1634585d7a4 Binary files /dev/null and b/assets/images/14.jpg differ diff --git a/assets/images/15.jpg b/assets/images/15.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19cdfe99bf418f53dde71fa3058d44e162a24258 Binary files /dev/null and b/assets/images/15.jpg differ diff --git a/assets/images/16.jpg b/assets/images/16.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a7c5ef33edf129ee657ba44eb730091a80520b22 Binary files /dev/null and b/assets/images/16.jpg differ diff --git a/assets/images/2.png b/assets/images/2.png new file mode 100644 index 0000000000000000000000000000000000000000..414c5c1fae9e3b3d8851dc27a2deb2585b835744 Binary files /dev/null and b/assets/images/2.png differ diff --git a/assets/images/3.jpg b/assets/images/3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a528f563d6da83b585b0d0fb5e46962177575435 Binary files /dev/null and b/assets/images/3.jpg differ diff --git a/assets/images/4.jpg b/assets/images/4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ff354903a75c7a79b98a68e2fe3b9af25fc220e Binary files /dev/null and b/assets/images/4.jpg differ diff --git a/assets/images/5.jpg b/assets/images/5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6e83d8a29f11a14e653d985b58466257660e1a74 Binary files /dev/null and b/assets/images/5.jpg differ diff --git a/assets/images/6.png b/assets/images/6.png new file mode 100644 index 0000000000000000000000000000000000000000..d787df19dc1384689f264d855d01f1520976f7f6 Binary files /dev/null and b/assets/images/6.png differ diff --git a/assets/images/7.jpeg b/assets/images/7.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..6dc6066941c849c8d2ef9491031291e8f90ef6bc Binary files /dev/null and b/assets/images/7.jpeg differ diff --git a/assets/images/8.png b/assets/images/8.png new file mode 100644 index 0000000000000000000000000000000000000000..d6d5d3a60ca11d7b037fe6c887416420c256a71b Binary files /dev/null and b/assets/images/8.png differ diff --git a/assets/images/9.jpg b/assets/images/9.jpg new file mode 100644 index 0000000000000000000000000000000000000000..03907e91630698587e8c325d3f9a3f29e3040077 Binary files /dev/null and b/assets/images/9.jpg differ diff --git a/assets/images/Hero.png b/assets/images/Hero.png new file mode 100644 index 0000000000000000000000000000000000000000..1d4b40e03652df828e30ee66dd4d63a668a44e76 Binary files /dev/null and b/assets/images/Hero.png differ diff --git a/assets/images/ramen.jpg b/assets/images/ramen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..649375db9c9a2b0e01319363ca4c0c68d68147d4 Binary files /dev/null and b/assets/images/ramen.jpg differ diff --git a/assets/readme/admin_homepage.png b/assets/readme/admin_homepage.png new file mode 100644 index 0000000000000000000000000000000000000000..bc9789748407dbbaf8b1c22270fa6d48fd79bea7 Binary files /dev/null and b/assets/readme/admin_homepage.png differ diff --git a/assets/readme/create_event.png b/assets/readme/create_event.png new file mode 100644 index 0000000000000000000000000000000000000000..a514b7fa621d5c84a4c14f387961b5a8c9758305 Binary files /dev/null and b/assets/readme/create_event.png differ diff --git a/assets/readme/delete_event.png b/assets/readme/delete_event.png new file mode 100644 index 0000000000000000000000000000000000000000..c9ec5e287ce8e09bbe272f213e5fdba9cd680967 Binary files /dev/null and b/assets/readme/delete_event.png differ diff --git a/assets/readme/edit_event.png b/assets/readme/edit_event.png new file mode 100644 index 0000000000000000000000000000000000000000..4022b4940daf1fe114706c1631ccffbdef4542d8 Binary files /dev/null and b/assets/readme/edit_event.png differ diff --git a/assets/readme/edit_profile.png b/assets/readme/edit_profile.png new file mode 100644 index 0000000000000000000000000000000000000000..38ff1ba33de3f4db18d9240435722a38fa0122f6 Binary files /dev/null and b/assets/readme/edit_profile.png differ diff --git a/assets/readme/history_page.png b/assets/readme/history_page.png new file mode 100644 index 0000000000000000000000000000000000000000..9c791ad6ca25cc54df1b16c115549d29032e696f Binary files /dev/null and b/assets/readme/history_page.png differ diff --git a/assets/readme/login.png b/assets/readme/login.png new file mode 100644 index 0000000000000000000000000000000000000000..88e66ea4654e2d4bc21094413c078309d4ae5f35 Binary files /dev/null and b/assets/readme/login.png differ diff --git a/assets/readme/purchase_ticket.png b/assets/readme/purchase_ticket.png new file mode 100644 index 0000000000000000000000000000000000000000..cb1bbf37d885ae16e4ed8907f327133198f7c19b Binary files /dev/null and b/assets/readme/purchase_ticket.png differ diff --git a/assets/readme/register.png b/assets/readme/register.png new file mode 100644 index 0000000000000000000000000000000000000000..51e51399ce0bc5ce63b4b85063825581f0753b41 Binary files /dev/null and b/assets/readme/register.png differ diff --git a/assets/readme/user_homepage.png b/assets/readme/user_homepage.png new file mode 100644 index 0000000000000000000000000000000000000000..6cbd80882af4f1608cd526ffbb5d09e8589d83dc Binary files /dev/null and b/assets/readme/user_homepage.png differ diff --git a/assets/readme/view_profile.png b/assets/readme/view_profile.png new file mode 100644 index 0000000000000000000000000000000000000000..5a60c988011977a380746139124b13fd35df8d9f Binary files /dev/null and b/assets/readme/view_profile.png differ diff --git a/assets/videos/video.mp4 b/assets/videos/video.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..ed139d6d50cc43f89c27392621f20d174d0bf051 Binary files /dev/null and b/assets/videos/video.mp4 differ diff --git a/db/connect.php b/db/connect.php new file mode 100644 index 0000000000000000000000000000000000000000..2f5aa5edf9b64799905e27d463d74b6dd509904e --- /dev/null +++ b/db/connect.php @@ -0,0 +1,19 @@ +<?php + $host = 'db'; + $dbname = 'tubes1_WBD'; + $user = 'root'; + $pass = 'm19e11'; + + try { + $db = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); + + // Execute SQL statements from init.sql + $sqlInit = file_get_contents(__DIR__ . '/init.sql'); + $db->exec($sqlInit); + + // echo "Connected successfully and database initialized!<br/>"; + } catch (PDOException $e) { + // echo "Connection failed: " . $e->getMessage(); + } + +?> diff --git a/db/data.php b/db/data.php new file mode 100644 index 0000000000000000000000000000000000000000..2b8c7e4f4a634b064b2c17efcd8c5b352b7c3b4c --- /dev/null +++ b/db/data.php @@ -0,0 +1,79 @@ +<?php +include 'connect.php'; + +// Make sure $db is defined and not null +if (isset($db)) { + try { + $db->beginTransaction(); + + // Insert events and tickets as before + $sqlEvent = <<<EOF + INSERT INTO events (event_name, event_stock, event_price, event_date, event_location, gambar, vid) VALUES + ('Music Concert', 2, 20, '2023-09-30 10:00:00', 'jakarta', 'assets/images/1.jpg', 'assets/videos/video.mp4'), + ('Art Exhibition', 2, 15, '2023-10-05 15:30:00', 'prancis', 'assets/images/2.png', 'assets/videos/video.mp4'), + ('Sports Tournament', 2, 40, '2023-11-12 18:45:00', 'italy', 'assets/images/3.jpg', 'assets/videos/video.mp4'), + ('Comedy Show', 2, 30, '2023-12-03 20:00:00', 'jakarta', 'assets/images/4.jpg', 'assets/videos/video.mp4'), + ('Tech Conference', 2, 60, '2024-01-18 14:15:00', 'ITB', 'assets/images/5.jpg', 'assets/videos/video.mp4'); + EOF; + + $sqlTicket = <<<EOF + INSERT INTO tickets (ticket_name, event_id) VALUES + ('General Admission', 1), + ('VIP Pass', 1), + ('Standard Ticket', 2), + ('Student Discount', 2), + ('Early Bird Special', 3), + ('Premium Access', 3), + ('Weekend Pass', 4), + ('Group Discount', 4), + ('Conference Pass', 5), + ('Exhibitor Pass', 5); + EOF; + + $db->exec($sqlEvent); + $db->exec($sqlTicket); + + // Dummy user data with hashed passwords + $userData = [ + ['John Doe', 'john_doe', 'john@example.com', password_hash('password_1', PASSWORD_DEFAULT), 1], + ['Jane Smith', 'jane_smith', 'jane@example.com', password_hash('password_2', PASSWORD_DEFAULT), 0], + ['Admin User', 'admin_user', 'admin@example.com', password_hash('password_3', PASSWORD_DEFAULT), 1], + ['Alice Johnson', 'alice', 'alice@example.com', password_hash('password_4', PASSWORD_DEFAULT), 0], + ['Bob Williams', 'bob', 'bob@example.com', password_hash('password_5', PASSWORD_DEFAULT), 0] + ]; + + $sqlUser = "INSERT INTO users (user_name, username, user_email, user_hashedPass, isAdmin) VALUES (?, ?, ?, ?, ?)"; + $stmt = $db->prepare($sqlUser); + + foreach ($userData as $user) { + $stmt->execute($user); + } + + $stmt->closeCursor(); + + // // Insert pembelian data + // $sqlPembelian = <<<EOF + // INSERT INTO pembelian (ticket_id, user_id, pembelian_created_time) VALUES + // (1, 1, '2023-10-01 12:30:00'), + // (3, 2, '2023-10-06 16:15:00'), + // (2, 3, '2023-11-15 20:00:00'), + // (5, 4, '2023-12-05 10:45:00'), + // (8, 5, '2024-01-20 08:30:00'); + // EOF; + + // $db->exec($sqlPembelian); + + // Commit the transaction + $db->commit(); + + echo "Successfully inserted dummy data.<br/>"; + } catch (PDOException $e) { + $db->rollBack(); + echo "Error: " . $e->getMessage(); + } + } else { + echo "Error: Database connection not established."; + } + + $db = null; +?> diff --git a/db/init.php b/db/init.php new file mode 100644 index 0000000000000000000000000000000000000000..d5efdd7fbee4a7978546eb4828d86c6a208c781c --- /dev/null +++ b/db/init.php @@ -0,0 +1,26 @@ +<?php +include 'connect.php'; + +$dotenvContents = file_get_contents(__DIR__ . '/../.env'); +$dotenvLines = explode("\n", $dotenvContents); + +foreach ($dotenvLines as $line) { + // Skip empty lines and comments + if (!empty($line) && strpos($line, '=') !== false && strpos($line, '#') !== 0) { + list($name, $value) = explode('=', $line, 2); + $_ENV[$name] = $value; + putenv("$name=$value"); + } +} + +error_reporting(E_ALL); +ini_set('display_errors', 1); + + +include 'reset.php'; +include 'data.php'; + +$db = null; + +// echo "Database initialization successful!"; +?> diff --git a/db/init.sql b/db/init.sql index 3b087236d19a989c211c70e52c70ed10aaecb105..2260e6011f4701b1ec4506bee5c96de4a4b8e595 100644 --- a/db/init.sql +++ b/db/init.sql @@ -2,26 +2,29 @@ CREATE DATABASE IF NOT EXISTS tubes1_WBD; USE tubes1_WBD; -CREATE TABLE IF NOT EXISTS event ( +CREATE TABLE IF NOT EXISTS events ( event_id INT AUTO_INCREMENT PRIMARY KEY, + event_name CHAR(255), event_stock INT, - event_created_time DATETIME, - event_details CHAR(255) + event_price INT, + event_date DATETIME, + event_location CHAR(255), + gambar VARCHAR(255), + vid VARCHAR(255) ); -CREATE TABLE IF NOT EXISTS ticket ( +CREATE TABLE IF NOT EXISTS tickets ( ticket_id INT AUTO_INCREMENT PRIMARY KEY, ticket_name CHAR(255), - ticket_price INT, event_id INT, - FOREIGN KEY (event_id) REFERENCES event(event_id) + FOREIGN KEY (event_id) REFERENCES events(event_id) ON DELETE SET NULL ); -CREATE TABLE IF NOT EXISTS user ( +CREATE TABLE IF NOT EXISTS users ( user_ID INT AUTO_INCREMENT PRIMARY KEY, user_name CHAR(255), - username CHAR(255), - user_email VARCHAR(255), + username CHAR(255) UNIQUE, + user_email VARCHAR(255) UNIQUE, user_hashedPass CHAR(255), isAdmin BOOLEAN ); @@ -31,6 +34,6 @@ CREATE TABLE IF NOT EXISTS pembelian ( ticket_id INT, user_id INT, pembelian_created_time DATETIME, - FOREIGN KEY (ticket_id) REFERENCES ticket(ticket_id), - FOREIGN KEY (user_id) REFERENCES user(user_ID) -); + FOREIGN KEY (ticket_id) REFERENCES tickets(ticket_id), + FOREIGN KEY (user_id) REFERENCES users(user_ID) ON DELETE SET NULL +); \ No newline at end of file diff --git a/db/reset.php b/db/reset.php new file mode 100644 index 0000000000000000000000000000000000000000..1e98b7a351e2e637d0fa059e489aad7e4f08635b --- /dev/null +++ b/db/reset.php @@ -0,0 +1,15 @@ +<?php + include 'connect.php'; + + try { + $db->exec("DROP TABLE IF EXISTS pembelian"); + $db->exec("DROP TABLE IF EXISTS tickets"); + $db->exec("DROP TABLE IF EXISTS events"); + $db->exec("DROP TABLE IF EXISTS users"); + + // echo "Successfully reset the database<br/>"; + } catch (PDOException $e) { + // echo "Error: " . $e->getMessage(); + } + $db = null; +?> \ No newline at end of file diff --git a/index.php b/index.php index 20d93818887f051e3914dbfd1195f1e6a4b97b68..d372ba3bbdeec82cdae50eb23accf17f57483518 100644 --- a/index.php +++ b/index.php @@ -1,32 +1,4 @@ -<!DOCTYPE html> -<html> - -<head> - <title>PHP TITLE</title> -</head> - -<body> - <h1>HAI HAI HAI HAI</h1> - <p>lalalalala</p> - - <?php - $host = 'db'; // Service name from docker-compose - $dbname = 'tubes1_WBD'; // Your database name - $user = 'root'; // Your database user - $pass = ''; // Your database password - - try { - $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); - echo "Connected successfully!"; - } catch (PDOException $e) { - echo "Connection failed: " . $e->getMessage(); - } - // PHP code - $phpVersion = phpversion(); - echo "<p>PHP Version: $phpVersion</p>"; - ?> - - <p>asdf</p> -</body> - -</html> \ No newline at end of file +<?php + include 'db/connect.php'; + header('Location:app/Views/home/home.php'); +?> \ No newline at end of file diff --git a/styles/auth.css b/styles/auth.css new file mode 100644 index 0000000000000000000000000000000000000000..f6674ca2e9bf738945037771ccc215d0c6cd06f3 --- /dev/null +++ b/styles/auth.css @@ -0,0 +1,65 @@ +body { + font-family: Arial, sans-serif; + background-color: #f8f8f8; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + margin: 0; +} + +.login-container, .register-container { + background-color: white; + padding: 20px; + border-radius: 5px; + width: 300px; + text-align: center; +} + +label { + display: block; + margin: 10px 0 5px 0; + text-align: left; +} + +input { + width: calc(100% - 20px); + padding: 10px; + margin-bottom: 10px; + border-radius: 5px; + border: 1px solid #ccc; +} + +button { + background-color: #97A5CE; /* Green */ + border: none; + color: white; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; + transition-duration: 0.4s; + cursor: pointer; +} + +button:hover { + background-color: white; + color: black; + border: 1px solid #97A5CE; +} + +a { + color: #97A5CE; +} + +a:hover { + text-decoration: underline; +} + +.checkbox-label { + display: flex; + margin: 15px 0; + text-align: left; +} \ No newline at end of file diff --git a/styles/container.css b/styles/container.css new file mode 100644 index 0000000000000000000000000000000000000000..1c1cfaa83b6f28beaaad168b595c1a72fe5de6f5 --- /dev/null +++ b/styles/container.css @@ -0,0 +1,54 @@ +.column { + margin: 20px; /* Add a top margin to avoid content overlay */ + padding: 20px; + align-items: center; + height: fit-content; + border: 5px solid transparent; + max-width: 900px; +} + +.row { + display: flex; /* Display children in a row */ + justify-content: space-between; /* Spread children evenly horizontally */ + align-items: center; /* Center children vertically */ + padding: 10px; /* Add padding for spacing */ +} + +.button-container { + display: flex; + justify-content: space-between; + align-items: center; +} + +.event-container { + padding: 10px; + margin-top: 20px; + width: fit-content; + border: 5px solid transparent; + background-color: lightblue; +} + +.history-container{ + padding: 10px; + margin-top: 20px; + width: fit-content; + border: 5px solid transparent; + background-color: lightblue; +} + +.main-content { + position: absolute; + top: 7%; + height: fit-content; + align-items: center; + width: fit-content; + border: 30px solid transparent; +} + +.profile-container { + padding: 10px; + margin-top: 50px; + width: fit-content; + border: 5px solid transparent; + align-items: center; +} \ No newline at end of file diff --git a/styles/dropdown.css b/styles/dropdown.css new file mode 100644 index 0000000000000000000000000000000000000000..bca90468181caa0f407c3624c5ebcaa0932fd166 --- /dev/null +++ b/styles/dropdown.css @@ -0,0 +1,9 @@ +.styled-select { + font-size: 14px; + height: 40px; + width: calc(100% - 20px); + padding: 10px; + margin-bottom: 10px; + border-radius: 5px; + border: 1px solid #ccc; +} diff --git a/styles/footer.css b/styles/footer.css new file mode 100644 index 0000000000000000000000000000000000000000..8ce7d67839a3310c5ea814dfda8fcd8b958fd8f7 --- /dev/null +++ b/styles/footer.css @@ -0,0 +1,12 @@ +.footer { + position: fixed; /* Set the navbar to fixed position */ + bottom: 0; /* Position the navbar at the bottom of the page */ + width: 100%; /* Full width */ + min-height: fit-content; + max-height: 20px; + background-color: navy; + color: white; + text-align: center; + padding: 10px; + height: 43px; + } \ No newline at end of file diff --git a/styles/form.css b/styles/form.css new file mode 100644 index 0000000000000000000000000000000000000000..d840b003b0df8629937a47836798cdc6c973a182 --- /dev/null +++ b/styles/form.css @@ -0,0 +1,51 @@ +/* Add this to your global.css or create a new CSS file */ + +.event-form, +.user-form { + max-width: 600px; + margin: 20px auto; + padding: 20px; + border: 1px solid #ccc; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.event-form form, +.user-form form { + margin-bottom: 20px; +} + +.event-form label, +.user-form label { + display: block; + margin-bottom: 8px; +} + +.event-form input, +.user-form input, +.event-form button, +.user-form button { + margin-bottom: 15px; +} + +.event-form #drop-area-create, +.event-form #drop-area-update, +.user-form #drop-area-create, +.user-form #drop-area-update { + margin-bottom: 15px; + padding: 10px; + border: 2px dashed #ccc; + border-radius: 8px; + text-align: center; +} + +.event-form img, +.user-form img { + max-width: 350px; + max-height: 350px; + margin-top: 10px; + border: 1px solid #ccc; + border-radius: 8px; +} + +/* Additional styling can be added based on your preference */ diff --git a/styles/global.css b/styles/global.css new file mode 100644 index 0000000000000000000000000000000000000000..eb011854c6380c6c4628d533228d495f81811ba9 --- /dev/null +++ b/styles/global.css @@ -0,0 +1,87 @@ +body, h1, h2, h3, p, img{ + margin: 0; + padding: 0; + border: 0; +} + +body { + box-sizing: border-box; + font-family: sans-serif; +} + +.hero { + position: relative; + width: 100%; + height: 250px; + z-index: -1; +} + +.hero img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.hero-title { + text-align: center; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 100; + font-family: sans-serif; + font-size: xxx-large; +} + +.index { + text-align: center; + margin: 0 auto; /* Center the .index content horizontally */ + max-width: 800px; /* Adjust the maximum width as needed */ + padding: 20px; /* Add some padding for better spacing */ +} + + +div.index div.events { + text-align: center; + margin-top: 20px; +} + +div.index div.events li { + margin-bottom: 100px; +} + +.index .button { + position: absolute; + right: 2%; + top: 10px; +} + +.button { + background-color: rgb(255, 0, 47); + padding: 10px 24px; + border-radius: 50px; + cursor: pointer; + transition: 0.3s; + display: inline-block; /* Adjust display property for proper centering */ + text-decoration: none; + color: white; +} + +.button:hover { + background-color: rgb(255, 156, 171); + color: white; +} + +.button:active { + background-color: rgb(240, 63, 96); +} + +.events { + text-align: center; /* Center text within the .events container */ + margin-top: 20px; +} + +.history { + text-align: center; /* Center text within the .events container */ + margin-top: 20px; +} \ No newline at end of file diff --git a/styles/navbar.css b/styles/navbar.css new file mode 100644 index 0000000000000000000000000000000000000000..96d0f06f46e23da3ac671d541cfa1e2986577018 --- /dev/null +++ b/styles/navbar.css @@ -0,0 +1,94 @@ +/* Navbar styles */ + +.navbar { + background-color: #333; + overflow: hidden; + position: fixed; + top: 0; + width: 100%; + z-index: 999999; +} + +.navbar ul { + list-style-type: none; + margin: 0; + margin-right: 30px; + padding: 0; + display: flex; + justify-content: flex-end; /* Align items to the right */ +} + +.navbar li { + text-align: center; + margin-left: 15px; /* Adjust the spacing between items */ +} + +.navbar a { + display: block; + padding: 15px; + text-decoration: none; + color: white; + transition: background-color 0.3s; +} + +.navbar a:hover { + background-color: #555; +} + +/* Adjustments for the Logout button */ +.navbar form { + margin: 0; +} + +.navbar .navbar-toggle form button { + background: none; + border: none; + padding: 15px; + text-decoration: none; + color: rgb(201, 7, 7); + cursor: pointer; + font-family: Arial, sans-serif; /* Adjust font family */ +} + +.navbar form button:hover { + background-color: #555; +} + +/* Responsive version */ +@media screen and (max-width: 600px) { + .navbar ul { + flex-direction: column; /* Stack items vertically for small screens */ + align-items: flex-end; /* Align items to the right */ + display: none; /* Hide the menu by default */ + position: absolute; + top: 60px; /* Adjust the top position to avoid overlapping with the header */ + background-color: #333; + width: 100%; + } + + .navbar ul.show { + display: flex; /* Show the menu when active */ + z-index: 9999999; + } + + .navbar li { + width: 100%; + text-align: right; /* Align text to the right in the menu */ + margin: 0; + } + + .navbar a:hover { + background-color: #555; + } + + .navbar-toggle { + display: block; + position: absolute; + top: 0; + right: 0; + padding: 15px; + cursor: pointer; + color: white; + background-color: #333; + } +} diff --git a/styles/pagination.css b/styles/pagination.css new file mode 100644 index 0000000000000000000000000000000000000000..2bf452fbe28aa63eed12f22422b24a9533899791 --- /dev/null +++ b/styles/pagination.css @@ -0,0 +1,28 @@ +/* Pagination container */ +.pagination { + display: flex; + justify-content: center; + margin-top: 20px; +} + +/* Pagination links (Previous, Next, Page numbers) */ +.pagination a { + color: #333; + text-decoration: none; + padding: 8px 16px; + margin: 0 5px; + border: 1px solid #ddd; + border-radius: 4px; +} + +/* Style for the current page */ +.pagination a.active { + background-color: #007BFF; + color: white; +} + +/* Hover effect for pagination links */ +.pagination a:hover { + background-color: #ddd; + color: #007BFF; +}