diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4db5489cc9dbdf763f3e1df72e970e780b5f7bdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +postgres\ +storage/note/img/ +storage/note/vid/ +seeding.lock \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..810634dbaa72827c347183c24a0dfd2818e6563b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM php:8.0-apache + +WORKDIR /var/www/html + +COPY index.php /var/www/html + +RUN apt-get update && apt-get install -y libpq-dev \ + && apt-get -y upgrade \ + && docker-php-ext-install pgsql pdo pdo_pgsql + +RUN a2enmod rewrite + +EXPOSE 80 diff --git a/README.md b/README.md index c3319447b7db2c22369b37cc8ed568c9bf0cc588..63666251993dd516d7776c196aec5bab4cdfd60b 100644 --- a/README.md +++ b/README.md @@ -1,92 +1,82 @@ -# Tugas-Besar-2-PHP +# README - Aplikasi Web Notes dan Reminders +## Deskripsi Aplikasi +Aplikasi web ini adalah platform untuk mencatat catatan dan pengingat. Pengguna dapat membuat, mengedit, dan menghapus catatan, serta menetapkan pengingat untuk catatan tertentu. Aplikasi ini memungkinkan pengguna untuk mengelola catatan mereka dengan mudah dan efisien. -## Getting started +## Requirement -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +Aplikasi ini memerlukan instalasi Docker untuk menjalankannya di lingkungan lokal. -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! +## Cara Instalasi -## Add your files +1. Clone repository ini ke direktori lokal Anda: -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: + ```bash + git clone https://gitlab.informatika.org/if3110-2023-01-27/tugas-besar-1.git + ``` -``` -cd existing_repo -git remote add origin https://gitlab.informatika.org/if3110-2023-01-27/tugas-besar-2-php.git -git branch -M main -git push -uf origin main -``` +2. Masuk ke direktori proyek: -## Integrate with your tools + ```bash + cd tugas-besar-1 + ``` +3. Pastikan tidak ada file "seeder.lock" pada folder /app/models/ setiap kali saat melakukan build. -- [ ] [Set up project integrations](https://gitlab.informatika.org/if3110-2023-01-27/tugas-besar-2-php/-/settings/integrations) +4. Jalankan aplikasi menggunakan Docker Compose: -## Collaborate with your team + ```bash + docker-compose up --build + ``` -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) + Aplikasi akan dibangun dan dijalankan di dalam wadah Docker. -## Test and Deploy +4. Buka browser dan akses aplikasi melalui `http://localhost:8008`. -Use the built-in continuous integration in GitLab. +## Cara Menjalankan Server -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) +Setelah mengikuti langkah instalasi di atas, aplikasi akan berjalan secara lokal pada `http://localhost:8008. Anda dapat mengaksesnya menggunakan browser. -*** +## Screenshot Tampilan Aplikasi -# Editing this README +.png) +.png) +.png) +.png) +.png) +.png) +.png) +.png) +.png) +.png) -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. -## Suggestions for a good README -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. +## Pembagian Tugas -## Name -Choose a self-explaining name for your project. +Pembagian tugas dalam proyek ini adalah sebagai berikut: -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. +1. Frontend / Client-side: + - Login: 13521167 + - Register:13521167 + - Home:13521143 + - Notes:13521160 + - Reminder:13521143 + - Profile Page:13521167 + - Admin Page:13521160 + - Error Page:13521143 + - Navbar:13521143 + - Responsive:13521160 + - Desain:13521143, 13521160, 13521167 -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. +2. Backend / Server-side: + - Login:13521143 + - Register:13521143 + - Home:13521143 + - Notes:13521160 + - Reminder:13521143 + - Profile Page:13521160, 13521167 + - Admin Page:13521160 + - Error Page:13521143 + - Navbar:13521143 + - Database & Docker: 13521143 -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/app/api/admin/delete.php b/app/api/admin/delete.php new file mode 100644 index 0000000000000000000000000000000000000000..87b71e842a024fb65ad541f8f22b79076b5187e0 --- /dev/null +++ b/app/api/admin/delete.php @@ -0,0 +1,26 @@ +<?php + +require_once '../../../config/config.php'; +require_once '../../models/User.php'; + +if (isset($_POST['id'])) { + $user_model = new UserModel(); + $rows = $user_model->deleteUserById($_POST['id']); + + if ($rows) { + http_response_code(200); + echo json_encode(array( + "message" => "User deleted successfully." + )); + } else { + http_response_code(500); + echo json_encode(array( + "message" => "User deletion failed." + )); + } +} else { + http_response_code(400); + echo json_encode(array( + "message" => "Bad request." + )); +} diff --git a/app/api/admin/getUserInfos.php b/app/api/admin/getUserInfos.php new file mode 100644 index 0000000000000000000000000000000000000000..ed5712fc3da8a48d20db9fac7a9c564d0babc6aa --- /dev/null +++ b/app/api/admin/getUserInfos.php @@ -0,0 +1,22 @@ +<?php + +// require_once __DIR__ . '/../../config/config.php'; +// require_once __DIR__ . '/../../app/models/Notes.php'; +require_once '../../../config/config.php'; +require_once '../../models/User.php'; + +try{ + $user_model = new UserModel(); +} catch (Exception $e){ + echo($e->getMessage()); +} + +$users = $user_model->getUserTableInfos(); + +if ($users != null) { + http_response_code(200); + echo json_encode($users); +} else { + http_response_code(200); + echo json_encode(array('status' => 'error', 'message' => 'End of list.')); +} diff --git a/app/api/notes/delete.php b/app/api/notes/delete.php new file mode 100644 index 0000000000000000000000000000000000000000..9ae71a69eec142e30a54c99808ced639f2ddc18e --- /dev/null +++ b/app/api/notes/delete.php @@ -0,0 +1,42 @@ +<?php + +require_once '../../../config/config.php'; +require_once '../../models/notes.php'; + +if (isset($_POST['id'])) { + + // if (isset($_SESSION['is_admin']) && (($userId == $_SESSION['user_id']) || $_SESSION['is_admin'] == 1)) { + $note_model = new NoteModel(); + $userId = $note_model->getNoteById($_POST['id']); + $note = $note_model->getNoteById($_POST['id']); + + $rows = $note_model->deleteNote($_POST['id']); + + if ($rows) { + $rm_video = 'rm ' . $note->video_path; + $rm_img = 'rm ' . $note->image_path; + exec($rm_video); + exec($rm_img); + http_response_code(200); + echo json_encode(array( + "message" => "Note deleted successfully." + )); + } else { + http_response_code(500); + echo json_encode(array( + "message" => "Note deletion failed." + )); + } + // } else { + // http_response_code(403); // Forbidden + // $msg = "Access denied. You are not authorized to access this resource."; + // echo json_encode(array( + // "message" => $msg + // )); + // } +} else { + http_response_code(400); + echo json_encode(array( + "message" => "Bad request." + )); +} diff --git a/app/api/notes/detail.php b/app/api/notes/detail.php new file mode 100644 index 0000000000000000000000000000000000000000..bdf9f3a10e765fe7af3a81d51fc5939ee3be07f7 --- /dev/null +++ b/app/api/notes/detail.php @@ -0,0 +1,33 @@ +<?php + +require_once '../../../config/config.php'; +require_once '../../models/notes.php'; + +if (isset($_GET['id'])) { + $note_model = new NoteModel(); + // $userId = $note_model->getUserByNoteId($id); + + // // if (isset($_SESSION['is_admin']) && (($userId == $_SESSION['user_id']) || $_SESSION['is_admin'] == 1)) { + $id = $_GET['id']; + $note = $note_model->getNoteById($id); + $tags = $note_model->getTagsByNoteId($id); + $note->tags = $tags; + + if ($note != null) { + http_response_code(200); + echo json_encode($note); + } else { + http_response_code(500); + echo json_encode(array("message" => "Something went wrong.")); + } + // } else { + // http_response_code(403); // Forbidden + // $msg = "Access denied. You are not authorized to access this resource."; + // echo json_encode(array( + // "message" => $msg + // )); + // } +} else { + http_response_code(400); + echo json_encode(array("message" => "Bad request.")); +} diff --git a/app/api/notes/edit.php b/app/api/notes/edit.php new file mode 100644 index 0000000000000000000000000000000000000000..818a05d3401319324126517630376964615491cd --- /dev/null +++ b/app/api/notes/edit.php @@ -0,0 +1,118 @@ +<?php +require_once '../../../config/config.php'; +require_once '../../models/notes.php'; + +if (isset($_POST['id'])) { + $note_model = new NoteModel(); + $userId = $note_model->getNoteById($_POST['id']); + + // if (isset($_SESSION['is_admin']) && (($userId == $_SESSION['user_id']) || $_SESSION['is_admin'] == 1)) { + $note = $note_model->getNoteById($_POST['id']); + $uploadOk = 0; + + if (isset($_FILES['file'])) { + $uploadOk = 1; + $img_dir = "../../storage/note/img/"; + $target_img_file = $img_dir . basename($_FILES["file"]["name"]); + $imgFileType = strtolower(pathinfo($target_img_file, PATHINFO_EXTENSION)); + + // Check file size + if ($_FILES["file"]["size"] > 10000000) { + echo "Sorry, your file is too large."; + $uploadOk = 0; + } + + // Allow certain file formats + if ($imgFileType != "png" && $imgFileType != "jpeg" && $imgFileType != "jpg") { + echo "Sorry, only png, jpg & jpeg files are allowed."; + $uploadOk = 0; + } + + // Check if $uploadOk is set to 0 by an error + if ($uploadOk == 0) { + echo "Sorry, note edit failed."; + + // if everything is ok, try to upload file + } else { + $st = move_uploaded_file($_FILES["file"]["tmp_name"], $target_img_file); + } + } + + if (isset($_FILES['file2'])) { + $uploadOk = 1; + $vid_dir = "../../storage/note/vid/"; + $target_vid_file = $vid_dir . basename($_FILES["file2"]["name"]); + $vidFileType = strtolower(pathinfo($target_vid_file, PATHINFO_EXTENSION)); + + // Check file size + if ($_FILES["file2"]["size"] > 10000000) { + echo "Sorry, your file is too large."; + $uploadOk = 0; + } + + // Allow certain video file formats + if ($vidFileType != "mp4" && $vidFileType != "webm" && $vidFileType != "ogv" && $vidFileType != "mov") { + echo "Sorry, only mp4, webm, ogv, and mov files are allowed."; + $uploadOk = 0; + } + + // Check if $uploadOk is set to 0 by an error + if ($uploadOk == 0) { + echo "Sorry, note edit failed."; + + // if everything is ok, try to upload file + } else { + $st = move_uploaded_file($_FILES["file2"]["tmp_name"], $target_vid_file); + } + } + + if (isset($_POST['title'])) { + $note->title = $_POST['title']; + } + if (isset($_POST['content'])) { + $note->content = $_POST['content']; + } + if (isset($_FILES["file"]) && $uploadOk) { + $note->image_path = $target_img_file; + } + if (isset($_FILES["file2"]) && $uploadOk) { + $note->video_path = $target_vid_file; + } + + $rows = $note_model->editNote($note); + $editedRows = 1; + + if (isset($_POST['content'])) { + $tags = $note_model->getTagsByNoteId($_POST['id']); + + if (isset($_POST['tags'])) { + // Hapus semua tag yang terkait dengan catatan tersebut + $deletedRows = $note_model->deleteTagsByNoteId($_POST['id']); + + // $editedRows = $note_model->insertTags($_POST['id'], $tags); + $editedRows = $note_model->insertTag($_POST['id'], $_POST['tags']); + } + } + + if ($rows && $editedRows) { + http_response_code(200); + $msg = "Note successfully edited."; + } else { + http_response_code(500); + $msg = "Note edit failed."; + } + // } else { + // http_response_code(403); // Forbidden + // $msg = "Access denied. You are not authorized to access this resource."; + // echo json_encode(array( + // "message" => $msg + // )); + // } +} else { + http_response_code(400); + $msg = "Bad request" . $_POST['title'] . " posxpask"; +} + +echo json_encode(array( + "message" => $msg +)); \ No newline at end of file diff --git a/app/api/notes/page.php b/app/api/notes/page.php new file mode 100644 index 0000000000000000000000000000000000000000..6fc242da074eecac7f34addcc76f182cb4b4f900 --- /dev/null +++ b/app/api/notes/page.php @@ -0,0 +1,41 @@ +<?php +require_once '../../../config/config.php'; +require_once '../../models/Notes.php'; + +$note_model = new NoteModel(); +$params = array(); + +// if (isset($_SESSION['is_admin']) && (($_GET['user_id'] == $_SESSION['user_id']) || $_SESSION['is_admin'] == 1)) { + if (isset($_GET['title'])) { + $title = $_GET['title']; + $params['title'] = $title; + } + if (isset($_GET['tags'])) { + $tags = $_GET['tags']; + $params['tags'] = $tags; + } + if (isset($_GET['user_id'])) { + $tags = $_GET['user_id']; + $params['user_id'] = $tags; + } + // $params['user_id'] = $_SESSION['user_id']; + + $count = $note_model->getCountNotes($params); + + if ($count) { + http_response_code(200); + echo json_encode($count); + } else { + http_response_code(500); + $msg = "Get count notes failed."; + echo json_encode(array( + "message" => $msg + )); + } +// } else { +// http_response_code(403); // Forbidden +// $msg = "Access denied. You are not authorized to access this resource."; +// echo json_encode(array( +// "message" => $msg +// )); +// } diff --git a/app/api/notes/search.php b/app/api/notes/search.php new file mode 100644 index 0000000000000000000000000000000000000000..bef239779df6ab86f80f62914c23028a0150e6fb --- /dev/null +++ b/app/api/notes/search.php @@ -0,0 +1,56 @@ +<?php + +// require_once __DIR__ . '/../../config/config.php'; +// require_once __DIR__ . '/../../app/models/Notes.php'; +require_once '../../../config/config.php'; +require_once '../../models/notes.php'; + +// if (isset($_SESSION['is_admin']) && (($_GET['user_id'] == $_SESSION['user_id']) || $_SESSION['is_admin'] == 1)) { + try{ + $note_model = new NoteModel(); + } catch (Exception $e){ + echo($e->getMessage()); + } + + $params = array(); + + if (isset($_GET['title'])) { + $title = $_GET['title']; + $params['title'] = $title; + } + if (isset($_GET['title'])) { + $title = $_GET['title']; + $params['title'] = $title; + } + if (isset($_GET['tags'])) { + $tags = $_GET['tags']; + $params['tags'] = $tags; + } + if (isset($_GET['sort'])) { + $sort = $_GET['sort']; + $params['sort'] = $sort; + } + if (isset($_GET['page'])) { + $page = $_GET['page']; + $params['page'] = $page; + } + $params['limit'] = 4; + $params['user_id'] = $_GET['user_id']; + + $notes = $note_model->findNotes($params); + + if ($notes != null) { + http_response_code(200); + echo json_encode($notes); + // echo json_encode(array('notes' => $notes, 'params' => $params['user_id'])); + } else { + http_response_code(200); + echo json_encode(array('status' => 'error', 'message' => 'End of list.')); + } +// } else { +// http_response_code(403); // Forbidden +// $msg = "Access denied. You are not authorized to access this resource."; +// echo json_encode(array( +// "message" => $msg +// )); +// } diff --git a/app/api/notes/upload.php b/app/api/notes/upload.php new file mode 100644 index 0000000000000000000000000000000000000000..df9e560871be05cc5fbbaf88a53081a5546fcc12 --- /dev/null +++ b/app/api/notes/upload.php @@ -0,0 +1,93 @@ +<?php + +require_once '../../../config/config.php'; +require_once '../../models/Notes.php'; + + +$img_dir = "../../../storage/note/img/"; +$target_img_file = $img_dir . basename($_FILES["file"]["name"]); +$saved_img_dir = "../../../storage/note/img/" . basename($_FILES["file"]["name"]); + +$vid_dir = "../../../storage/note/vid/"; +$target_vid_file = $vid_dir . basename($_FILES["file2"]["name"]); +$saved_vid_dir = "../../../storage/note/vid/" . basename($_FILES["file2"]["name"]); + +if (isset($_POST['title']) && isset($_POST['content']) && isset($_POST['user_id']) && isset($_POST['tags']) && isset($_FILES['file']) && isset($_FILES['file2'])) { + // if (isset($_SESSION['is_admin']) && (($_POST['user_id'] == $_SESSION['user_id']) || $_SESSION['is_admin'] == 1)) { + $note_model = new NoteModel(); + + $next_id = $note_model->getLatestNoteId()->note_id + 1; + $uploadOk = 1; + $vidFileType = strtolower(pathinfo($target_vid_file, PATHINFO_EXTENSION)); + $imgFileType = strtolower(pathinfo($target_img_file, PATHINFO_EXTENSION)); + + // Check file size + if ($_FILES["file"]["size"] > 10000000) { + echo "Sorry, your file is too large."; + $uploadOk = 0; + } + if ($_FILES["file2"]["size"] > 10000000) { + echo "Sorry, your file is too large."; + $uploadOk = 0; + } + + // Allow certain video file formats + if ($vidFileType != "mp4" && $vidFileType != "webm" && $vidFileType != "ogv" && $vidFileType != "mov") { + echo "Sorry, only mp4, webm, ogv, and mov files are allowed."; + $uploadOk = 0; + } + + if ($imgFileType != "png" && $imgFileType != "jpeg" && $imgFileType != "jpg") { + echo "Sorry, only png, jpg & jpeg files are allowed."; + $uploadOk = 0; + } + + // Check if $uploadOk is set to 0 by an error + if ($uploadOk == 0) { + http_response_code(500); + echo "Sorry, your file was not uploaded."; + // if everything is ok, try to upload file + } else { + $st = move_uploaded_file($_FILES["file"]["tmp_name"], $target_img_file); + $st2 = move_uploaded_file($_FILES["file2"]["tmp_name"], $target_vid_file); + + + // $cmd = 'ffprobe -i "' . $target_vid_file . '" -show_entries format=duration -v quiet -of csv="p=0"'; + // error_log(print_r($cmd, true)); + // // $cmd = 'mp3info -p "%S" ' . $target_vid_file; + // $duration = intval(exec($cmd)); + // error_log(print_r($duration, true)); + + $dataparams = array( + 'title' => $_POST['title'], + 'content' => $_POST['content'], + 'user_id' => $_POST['user_id'], + 'video_path' => $saved_vid_dir, + 'image_path' => $saved_img_dir + ); + $noteId = $note_model->insertNote($dataparams); + // $rows = $note_model->insertTags(9, $_POST['tags']); + // $rows = $note_model->insertTag(52, $_POST['tags']); + $rows = $note_model->insertTag($next_id, $_POST['tags']); + + if ($noteId && $rows && $uploadOk == 1) { + http_response_code(200); + echo json_encode(array("message" => "Note uploaded successfully")); + } else { + echo($_POST['user_id']. "\n"); + echo json_encode(array("message" => "Note upload failed\n" . " $next_id")); + + http_response_code(500); + } + } + // } else { + // http_response_code(403); // Forbidden + // $msg = "Access denied. You are not authorized to access this resource."; + // echo json_encode(array( + // "message" => $msg + // )); + // } +} else { + http_response_code(400); + echo json_encode(array("message" => "Bad request")); +} diff --git a/app/api/reminder/addReminder.php b/app/api/reminder/addReminder.php new file mode 100644 index 0000000000000000000000000000000000000000..7000c16ecce47800de2a0e24ab40c48c4e4717fa --- /dev/null +++ b/app/api/reminder/addReminder.php @@ -0,0 +1,29 @@ +<?php + +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../models/ReminderModel.php'; + +$reminder_model = new ReminderModel(); + +if (isset($_POST['user_id']) && isset($_POST['title']) && isset($_POST['deadline']) && isset($_POST['tasks']) && isset($_POST['category'])) { + $dataparams = array( + 'user_id' => $_POST['user_id'], + 'title' => $_POST['title'], + 'deadline' => $_POST['deadline'], + 'tasks' => $_POST['tasks'], + 'category' => $_POST['category'] + ); + + $reminder = $reminder_model->insertReminder($dataparams); + + if ($reminder != null) { + http_response_code(200); + echo json_encode($reminder); + } else { + http_response_code(500); + echo json_encode(array("message" => "Something went wrong.")); + } + } else { + http_response_code(400); + echo json_encode(array("message" => "Bad request." . ($_POST['user_id']) . ($_POST['title']) . ($_POST['deadline']) . ($_POST['tasks']) . ($_POST['category']))); +} diff --git a/app/api/reminder/deleteReminder.php b/app/api/reminder/deleteReminder.php new file mode 100644 index 0000000000000000000000000000000000000000..2d87a4442673bbc4152ec30c736691562a84fd45 --- /dev/null +++ b/app/api/reminder/deleteReminder.php @@ -0,0 +1,28 @@ +<?php + +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../models/ReminderModel.php'; + +$reminder_model = new ReminderModel(); + +if (isset($_POST['reminder_id'])) { + $id = $_POST['reminder_id']; + $category = $reminder_model->getCategoryByReminderId($id); + $dataparams = array( + 'reminder_id' => $_POST['reminder_id'], + 'category' => $category->category_name + ); + + $reminder = $reminder_model->deleteReminder($dataparams); + + if ($reminder) { + http_response_code(200); + echo json_encode($reminder); + } else { + http_response_code(500); + echo json_encode(array("message" => "Something went wrong.")); + } +} else { + http_response_code(400); + echo json_encode(array("message" => "Bad request.")); +} diff --git a/app/api/reminder/detailReminders.php b/app/api/reminder/detailReminders.php new file mode 100644 index 0000000000000000000000000000000000000000..19568ffcff7b00c0e066751cdbf0a24a93723ef0 --- /dev/null +++ b/app/api/reminder/detailReminders.php @@ -0,0 +1,24 @@ +<?php + +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../models/ReminderModel.php'; + +$reminder_model = new ReminderModel(); + +if (isset($_GET['id'])) { + $id = $_GET['id']; + $reminder = $reminder_model->getReminderById($id); + $category = $reminder_model->getCategoryByReminderId($id); + $reminder->category = $category; + + if ($reminder != null) { + http_response_code(200); + echo json_encode($reminder); + } else { + http_response_code(500); + echo json_encode(array("message" => "Something went wrong.")); + } +} else { + http_response_code(400); + echo json_encode(array("message" => "Bad request.")); +} diff --git a/app/api/reminder/editReminder.php b/app/api/reminder/editReminder.php new file mode 100644 index 0000000000000000000000000000000000000000..b65ce96de63f50e57506e0176ef51a93399bc47e --- /dev/null +++ b/app/api/reminder/editReminder.php @@ -0,0 +1,30 @@ +<?php + +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../models/ReminderModel.php'; + +$reminder_model = new ReminderModel(); + +if (isset($_POST['reminder_id']) && isset($_POST['user_id']) && isset($_POST['title']) && isset($_POST['deadline']) && isset($_POST['tasks']) && isset($_POST['category'])) { + $dataparams = array( + 'reminder_id' => $_POST['reminder_id'], + 'user_id' => $_POST['user_id'], + 'title' => $_POST['title'], + 'deadline' => $_POST['deadline'], + 'tasks' => $_POST['tasks'], + 'category' => $_POST['category'] + ); + + $reminder = $reminder_model->updateReminder($dataparams); + + if ($reminder != null) { + http_response_code(200); + echo json_encode($reminder); + } else { + http_response_code(500); + echo json_encode(array("message" => "Something went wrong.")); + } + } else { + http_response_code(400); + echo json_encode(array("message" => "Bad request.")); +} diff --git a/app/api/reminder/filterReminder.php b/app/api/reminder/filterReminder.php new file mode 100644 index 0000000000000000000000000000000000000000..13b78022beece2ac1f401ca49a00e42de1bbd3ee --- /dev/null +++ b/app/api/reminder/filterReminder.php @@ -0,0 +1,28 @@ +<?php + +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../models/ReminderModel.php'; + +try{ + $reminder_model = new ReminderModel(); +} catch (Exception $e){ + echo($e->getMessage()); +} +$params = array(); + +if (isset($_GET['category_name'])) { + $category_name = $_GET['category_name']; + $params['category_name'] = $category_name; +} + +$params['limit'] = 1; + +$reminders = $reminder_model->filterReminders($params); + +if ($reminders != null) { + http_response_code(200); + echo json_encode($reminders); +} else { + http_response_code(200); + echo json_encode(array('status' => 'error', 'message' => 'End of list.')); +} diff --git a/app/api/reminder/sortReminders.php b/app/api/reminder/sortReminders.php new file mode 100644 index 0000000000000000000000000000000000000000..0ef6aa59618d2fa5af75fc8bd297f3b303411afe --- /dev/null +++ b/app/api/reminder/sortReminders.php @@ -0,0 +1,27 @@ +<?php + +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../models/ReminderModel.php'; + +$reminder_model = new ReminderModel(); + +if (isset($_GET['id'])) { + $user_id = $_GET['id']; + $allReminders = $reminder_model->getReminderByUserId($user_id); + foreach ($allReminders as $reminder){ + $category = $reminder_model->getCategoryByReminderId($reminder->reminder_id); + $reminder->category = $category; + } + + + if ($allReminders != null) { + http_response_code(200); + echo json_encode($allReminders); + } else { + http_response_code(500); + echo json_encode(array("message" => "Something went wrong.")); + } +} else { + http_response_code(400); + echo json_encode(array("message" => "Bad request.")); +} diff --git a/app/api/user/edit.php b/app/api/user/edit.php new file mode 100644 index 0000000000000000000000000000000000000000..86989329e317f11a120b8ed37305d86ad3d3714c --- /dev/null +++ b/app/api/user/edit.php @@ -0,0 +1,28 @@ +<?php + +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../models/User.php'; + +$user_model = new UserModel(); + +if (isset($_POST['user_id']) && isset($_POST['email']) && isset($_POST['password'])) { + $dataparams = array( + 'user_id' => $_POST['user_id'], + 'email' => $_POST['email'], + 'password' => $_POST['password'] + ); + + $user = $user_model->updateUser($dataparams); + + if ($user != null) { + http_response_code(200); + echo json_encode($user); + } else { + http_response_code(500); + echo json_encode(array("message" => "Something went wrong.")); + } + } else { + http_response_code(400); + // echo json_encode(array("message" => "Bad request.", "post" => `$dataparams`)); + echo json_encode(array("message" => 'uid ' . $_POST['user_id'] . 'password ' . $_POST['password'] . 'email ' . $_POST['email'])); +} diff --git a/app/api/user/login.php b/app/api/user/login.php new file mode 100644 index 0000000000000000000000000000000000000000..c07fbe50e4c72362aff927f523df5748264e8332 --- /dev/null +++ b/app/api/user/login.php @@ -0,0 +1,29 @@ +<?php +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../core/App.php'; +require_once __DIR__ . '/../../models/user.php'; + +session_start(); +if (isset($_POST['email'])) { + + $user_model = new UserModel(); + $email = $_POST['email']; + $password = $_POST['password']; + + $result = $user_model->getUserByEmail($email); + if ($result == null) { + $_SESSION['error'] = "Invalid email or password"; + header('location: ../../../?Login'); + } else { + if (password_verify($password, $result->password)) { + $_SESSION['user_id'] = $result->user_id; + $_SESSION['username'] = $result->username; + $_SESSION['is_admin'] = $result->is_admin ? 1 : 0; + header('Location: ../../../?Home'); + } else { + $_SESSION['error'] = "Invalid email or password"; + header('location: ../../../?Login'); + } + } +} +?> diff --git a/app/api/user/logout.php b/app/api/user/logout.php new file mode 100644 index 0000000000000000000000000000000000000000..d5fbe71d6c857727d74c5d4d7b5d2ee90dfa3b71 --- /dev/null +++ b/app/api/user/logout.php @@ -0,0 +1,7 @@ +<?php +session_start(); + +if (isset($_SESSION['username']) || isset($_SESSION['user_id'])) { + session_unset(); +} +header('Location: ../../../?Login'); \ No newline at end of file diff --git a/app/api/user/register.php b/app/api/user/register.php new file mode 100644 index 0000000000000000000000000000000000000000..68af54655b546fa1649eace3edb8d8fe772000ca --- /dev/null +++ b/app/api/user/register.php @@ -0,0 +1,25 @@ +<?php + +require_once __DIR__ . '/../../../config/config.php'; +require_once __DIR__ . '/../../core/App.php'; +require_once __DIR__ . '/../../models/user.php'; + +$user_model = new UserModel(); + + +if (isset($_POST['username']) && isset($_POST['password']) && isset($_POST['email'])) { + $dataparams = array( + 'username' => $_POST['username'], + 'password' => $_POST['password'], + 'email' => $_POST['email'] +); + + $rows = $user_model->register($dataparams); + + if ($rows) { + header('Location: ../../../?Login'); + } else { + $_SESSION['error'] = "Something went wrong"; + header('Location: ../../../?Register'); + } +} \ No newline at end of file diff --git a/app/controllers/Admin.php b/app/controllers/Admin.php new file mode 100644 index 0000000000000000000000000000000000000000..ce06a2a683bd6821a5c41c36bb766ba2c8009975 --- /dev/null +++ b/app/controllers/Admin.php @@ -0,0 +1,96 @@ +<?php +require_once __DIR__ . '/../models/TagsModel.php'; +require_once __DIR__ . '/../models/Notes.php'; +require_once __DIR__ . '/../models/reminderModel.php'; + +// class Admin extends Controller { +// public function index() { +// if(isset($_SESSION['is_admin'])){ +// if ($_SESSION['is_admin'] == 1) { +// $this->view('admin/index'); +// } else { +// header('Location: /?home'); +// } +// } +// else { +// header('Location: /?home'); +// } +// } +// } + +class Admin extends Controller { + public function __construct() { + // Pemeriksaan is_admin di sini + if (!isset($_SESSION['is_admin']) || $_SESSION['is_admin'] != 1) { + header('Location: /?home'); + } + } + + public function index() { + $is_admin = $_SESSION['is_admin']; + $this->view('admin/index', array('is_admin' => $is_admin)); + } + + public function searchnotes($user_id = 2, $path = "&page=1") { + $tag_model = new TagsModel(); + + $allTagsData = $tag_model->getAllTags(); + $is_admin = $_SESSION['is_admin']; + $this->view('notes/index', array('path' => $path, 'allTagsData' => $allTagsData, 'user_id' => $user_id, 'is_admin' => $is_admin)); + } + + public function notecontent($id) { + $is_admin = $_SESSION['is_admin']; + $this->view('notes/content', array('id' => $id, 'is_admin' => $is_admin)); + } + + public function editnote($id) { + $tag_model = new TagsModel(); + + $allTagsData = $tag_model->getAllTags(); + // $user_id = $_SESSION['user_id']; + $is_admin = $_SESSION['is_admin']; + $this->view('notes/editnote', array('id' => $id, 'allTagsData' => $allTagsData, 'is_admin' => $is_admin)); + } + + public function addnote($user_id) { + $tag_model = new TagsModel(); + $note_model = new NoteModel(); + + $allTagsData = $tag_model->getAllTags(); + $next_id = $note_model->getLatestNoteId($user_id); + $is_admin = $_SESSION['is_admin']; + $this->view('notes/addnote', array('next_id' => $next_id->note_id, 'allTagsData' => $allTagsData, 'user_id' => $user_id, 'is_admin' => $is_admin)); + } + + public function reminder($user_id) { + $reminder_model = new ReminderModel(); + $tag_model = new TagsModel(); + + // $user_id = $_SESSION['user_id']; + + $allRemindersData = $reminder_model->getReminderByUserId($user_id); + $allTagsData = $tag_model->getAllTags(); + $is_admin = $_SESSION['is_admin']; + foreach ($allRemindersData as $reminder){ + $category = $reminder_model->getCategoryByReminderId($reminder->reminder_id); + $reminder->category = $category; + } + $this->view('reminder/index', ['allRemindersData' => $allRemindersData, 'allTagsData' => $allTagsData, 'user_id' => $user_id, 'is_admin' => $is_admin]); + } + + public function edituser($user_id) { + $reminder_model = new ReminderModel(); + $tag_model = new TagsModel(); + + $allRemindersData = $reminder_model->getReminderByUserId($user_id); + $allTagsData = $tag_model->getAllTags(); + + foreach ($allRemindersData as $reminder){ + $category = $reminder_model->getCategoryByReminderId($reminder->reminder_id); + $reminder->category = $category; + } + $this->view('profilepage/index', ['allRemindersData' => $allRemindersData, 'allTagsData' => $allTagsData, 'user_id' => $user_id]); + } + +} \ No newline at end of file diff --git a/app/controllers/Errors.php b/app/controllers/Errors.php new file mode 100644 index 0000000000000000000000000000000000000000..b548f20f59cd6bdd2c075da44746fd70894f13bf --- /dev/null +++ b/app/controllers/Errors.php @@ -0,0 +1,8 @@ +<?php + +class Errors extends Controller { + public function index() { + $user_id = $_SESSION['user_id']; + $this->view('error/index', array("user_id" => $user_id)); + } +} \ No newline at end of file diff --git a/app/controllers/Home.php b/app/controllers/Home.php new file mode 100644 index 0000000000000000000000000000000000000000..727ba82ac38183ee651991a176e42088bd06374b --- /dev/null +++ b/app/controllers/Home.php @@ -0,0 +1,16 @@ +<?php + +require_once __DIR__ . '/../models/notes.php'; +require_once __DIR__ . '/../models/ReminderModel.php'; +class Home extends Controller { + public function index() { + $note_model = new NoteModel(); + $reminder_model = new ReminderModel(); + + $count_notes = count($note_model->getNotesAndTagsByUserId($_SESSION['user_id'])); + $count_reminders= count($reminder_model->getReminderByUserId($_SESSION['user_id'])); + $user_info = $_SESSION['username']; + $is_admin = $_SESSION['is_admin']; + $this->view('home/index', ['count_notes' => $count_notes, 'count_reminders' => $count_reminders, 'user_info' => $user_info, 'is_admin' => $is_admin]); + } +} \ No newline at end of file diff --git a/app/controllers/Login.php b/app/controllers/Login.php new file mode 100644 index 0000000000000000000000000000000000000000..8e80c75f3ef722658b069cdc555601ab0abdee26 --- /dev/null +++ b/app/controllers/Login.php @@ -0,0 +1,7 @@ +<?php + +class Login extends Controller { + public function index() { + $this->view('login/index'); + } +} \ No newline at end of file diff --git a/app/controllers/Notes.php b/app/controllers/Notes.php new file mode 100644 index 0000000000000000000000000000000000000000..f6e2cdccaa665b7a3ad0ac1f1e6c433a2af00ef0 --- /dev/null +++ b/app/controllers/Notes.php @@ -0,0 +1,41 @@ +<?php + +require_once __DIR__ . '/../models/TagsModel.php'; +require_once __DIR__ . '/../models/Notes.php'; + +class Notes extends Controller { + public function index($path = "&page=1") { + $tag_model = new TagsModel(); + + $allTagsData = $tag_model->getAllTags(); + $user_id = $_SESSION['user_id']; + $is_admin = $_SESSION['is_admin']; + $this->view('notes/index', array('path' => $path, 'allTagsData' => $allTagsData, 'user_id' => $user_id, 'is_admin' => $is_admin)); + } + + public function content($id) { + $user_id = $_SESSION['user_id']; + $is_admin = $_SESSION['is_admin']; + $this->view('notes/content', array('id' => $id, 'user_id' => $user_id, 'is_admin' => $is_admin)); + } + + public function edit($id) { + $tag_model = new TagsModel(); + + $allTagsData = $tag_model->getAllTags(); + $user_id = $_SESSION['user_id']; + $is_admin = $_SESSION['is_admin']; + $this->view('notes/editnote', array('id' => $id, 'allTagsData' => $allTagsData, 'user_id' => $user_id,'is_admin' => $is_admin)); + } + + public function addnote() { + $tag_model = new TagsModel(); + $note_model = new NoteModel(); + + $allTagsData = $tag_model->getAllTags(); + $next_id = $note_model->getLatestNoteId(); + $user_id = $_SESSION['user_id']; + $is_admin = $_SESSION['is_admin']; + $this->view('notes/addnote', array('next_id' => $next_id->note_id, 'allTagsData' => $allTagsData, 'user_id' => $user_id, 'is_admin' => $is_admin)); + } +} \ No newline at end of file diff --git a/app/controllers/Profile.php b/app/controllers/Profile.php new file mode 100644 index 0000000000000000000000000000000000000000..cc72148e6289c266f4e167c05747603e3ab77436 --- /dev/null +++ b/app/controllers/Profile.php @@ -0,0 +1,21 @@ +<?php + +require_once __DIR__ . '/../models/reminderModel.php'; +require_once __DIR__ . '/../models/TagsModel.php'; +class Profile extends Controller { + public function index() { + $reminder_model = new ReminderModel(); + $tag_model = new TagsModel(); + + $user_id = $_SESSION['user_id']; + + $allRemindersData = $reminder_model->getReminderByUserId($user_id); + $allTagsData = $tag_model->getAllTags(); + + foreach ($allRemindersData as $reminder){ + $category = $reminder_model->getCategoryByReminderId($reminder->reminder_id); + $reminder->category = $category; + } + $this->view('profilepage/index', ['allRemindersData' => $allRemindersData, 'allTagsData' => $allTagsData, 'user_id' => $user_id]); + } +} \ No newline at end of file diff --git a/app/controllers/Register.php b/app/controllers/Register.php new file mode 100644 index 0000000000000000000000000000000000000000..382a5428ecd69268961d3ae9c55d5d0c7edb9230 --- /dev/null +++ b/app/controllers/Register.php @@ -0,0 +1,7 @@ +<?php + +class Register extends Controller { + public function index() { + $this->view('Register/index'); + } +} \ No newline at end of file diff --git a/app/controllers/reminder.php b/app/controllers/reminder.php new file mode 100644 index 0000000000000000000000000000000000000000..b8b615fc3eac6bff8ff07e2354d0bfa8da1b5933 --- /dev/null +++ b/app/controllers/reminder.php @@ -0,0 +1,22 @@ +<?php + +require_once __DIR__ . '/../models/reminderModel.php'; +require_once __DIR__ . '/../models/TagsModel.php'; +class Reminder extends Controller { + public function index() { + $reminder_model = new ReminderModel(); + $tag_model = new TagsModel(); + + $user_id = $_SESSION['user_id']; + $is_admin = $_SESSION['is_admin']; + + $allRemindersData = $reminder_model->getReminderByUserId($user_id); + $allTagsData = $tag_model->getAllTags(); + + foreach ($allRemindersData as $reminder){ + $category = $reminder_model->getCategoryByReminderId($reminder->reminder_id); + $reminder->category = $category; + } + $this->view('reminder/index', ['allRemindersData' => $allRemindersData, 'allTagsData' => $allTagsData, 'user_id' => $user_id, 'is_admin' => $is_admin]); + } +} \ No newline at end of file diff --git a/app/core/App.php b/app/core/App.php new file mode 100644 index 0000000000000000000000000000000000000000..cd449fda2ceff78892e047885e19e6aed479ca3c --- /dev/null +++ b/app/core/App.php @@ -0,0 +1,49 @@ +<?php + +class App { + protected $controller = 'Home'; + protected $method = 'index'; + protected $params = []; + + public function __construct(){ + $url = $this->parseURL(); + if ((!isset($_SESSION['username']) && !isset($_COOKIE['GUEST'])) && !empty($url) && $url[0] != 'register'){ + $this->controller = 'Login'; + } else { + if (file_exists('app/controllers/' . $url[0] . '.php')) { + $this->controller = $url[0]; + unset($url[0]); + } + else if ($url[0] == '') { + $this->controller = 'Login'; + } + else { + $this->controller = 'Errors'; + } + } + require_once 'app/controllers/' . $this->controller . '.php'; + $this->controller = new $this->controller; + if (isset($url[1])) { + if (method_exists($this->controller, $url[1])) { + $this->method = $url[1]; + unset($url[1]); + } + } + if ($url) { + $this->params = array_values($url); + } + call_user_func_array([$this->controller, $this->method], $this->params); + + } + + public function parseUrl(){ + if (isset($_SERVER['REQUEST_URI'])) { + $url = rtrim($_SERVER['REQUEST_URI'], '/'); + $url = ltrim($url, 'public/'); + $url = filter_var($url, FILTER_SANITIZE_URL); + $url = ltrim($url, '?'); + $url = explode('/', $url); + return $url; + } + } +} \ No newline at end of file diff --git a/app/core/Controller.php b/app/core/Controller.php new file mode 100644 index 0000000000000000000000000000000000000000..3365ccf568fd1523bdcd2d5d8ac15041e0668173 --- /dev/null +++ b/app/core/Controller.php @@ -0,0 +1,12 @@ +<?php + +class Controller{ + public function view($view, $data = []){ + if (file_exists('app/views/' . $view . '.php')){ + require_once __DIR__ . '/../views/' . $view . '.php'; + } else { + echo('app/views/' . $view . '.php'); + die('Error, does not exist'); + } + } +} \ No newline at end of file diff --git a/app/core/DataSeeder.php b/app/core/DataSeeder.php new file mode 100644 index 0000000000000000000000000000000000000000..4b234201a8dc78e5531f04386a32f67c0509b832 --- /dev/null +++ b/app/core/DataSeeder.php @@ -0,0 +1,126 @@ +<?php + +require_once __DIR__ . '/Database.php'; + +class DataSeeder { + public function seedDatas() { + $database = new Database(); + + $database->query("INSERT INTO users (username, email, password, is_admin) VALUES (:username, :email, :password, :is_admin) ON CONFLICT (username) DO NOTHING"); + $database->bind(':username', 'user1'); + $database->bind(':email', 'user1@example.com'); + $database->bind(':password', password_hash('password123', PASSWORD_DEFAULT)); + $database->bind(':is_admin', false); + $database->execute(); + + $database->query("INSERT INTO users (username, email, password, is_admin) VALUES (:username, :email, :password, :is_admin) ON CONFLICT (username) DO NOTHING"); + $database->bind(':username', 'useradmin'); + $database->bind(':email', 'useradmin@example.com'); + $database->bind(':password', password_hash('password123', PASSWORD_DEFAULT)); + $database->bind(':is_admin', true); + $database->execute(); + + $database->query("INSERT INTO users (username, email, password, is_admin) VALUES (:username, :email, :password, :is_admin) ON CONFLICT (username) DO NOTHING"); + $database->bind(':username', 'user3'); + $database->bind(':email', 'user3@example.com'); + $database->bind(':password', password_hash('password123', PASSWORD_DEFAULT)); + $database->bind(':is_admin', false); + $database->execute(); + + // Seed tags table + $tags = ['All', 'Internship', 'University', 'Hobby']; + foreach ($tags as $tag) { + $database->query("INSERT INTO tags (tag_name) VALUES (:tag_name) ON CONFLICT (tag_name) DO NOTHING"); + $database->bind(':tag_name', $tag); + $database->execute(); + } + + $remindersData = [ + [ + 'user_id' => 1, + 'title' => 'Reminder 1', + 'deadline' => '14 Sep 2023 12:00', + 'tasks' => 'Task for reminder 1', + 'category' => 'Internship', + ], + [ + 'user_id' => 1, + 'title' => 'Reminder 2', + 'deadline' => '10 Sep 2023 12:00', + 'tasks' => 'Task for reminder 1', + 'category' => 'Internship', + ], + [ + 'user_id' => 1, + 'title' => 'Reminder 3', + 'deadline' => '12 Sep 2023 12:00', + 'tasks' => 'Task for reminder 1', + 'category' => 'Internship', + ], + [ + 'user_id' => 1, + 'title' => 'Reminder 4', + 'deadline' => '10 Oct 2023 12:00', + 'tasks' => 'Task for reminder 1', + 'category' => 'University', + ], + ]; + + + foreach ($remindersData as $reminder) { + $database->query("SELECT reminder_id FROM reminders WHERE user_id = :user_id AND title = :title AND deadline = :deadline AND tasks = :tasks"); + $database->bind(':user_id', $reminder['user_id']); + $database->bind(':title', $reminder['title']); + $database->bind(':deadline', $reminder['deadline']); + $database->bind(':tasks', $reminder['tasks']); + + $result = $database->fetch(); + + if ($result) { + continue; + } + + $database->query("INSERT INTO reminders (user_id, title, deadline, tasks) VALUES (:user_id, :title, :deadline, :tasks)"); + $database->bind(':user_id', $reminder['user_id']); + $database->bind(':title', $reminder['title']); + $database->bind(':deadline', $reminder['deadline']); + $database->bind(':tasks', $reminder['tasks']); + + $database->execute(); + + $reminderId = $database->lastInsertId(); + + $database->query("INSERT INTO category_reminders (category_name, reminder_id) VALUES (:category_name, :reminder_id)"); + $database->bind(':category_name', $reminder['category']); + $database->bind(':reminder_id', $reminderId); + $database->execute(); + } + + $imagePath = "../../../storage/note/img/pop.png"; + $videoPath = "../../../storage/note/vid/Visualisasi mimpi.mp4"; + $user_id = 3; + $tag_name = "University"; + + for ($i = 0; $i < 10000; $i++) { + + $title = "Dummy Note $i"; + $content = "This is a dummy note content for reminder $i."; + + $database->query("INSERT INTO notes (user_id, title, content, image_path, video_path) VALUES (:user_id, :title, :content, :image_path, :video_path)"); + $database->bind(':user_id', $user_id); + $database->bind(':title', $title); + $database->bind(':content', $content); + $database->bind(':image_path', $imagePath); + $database->bind(':video_path', $videoPath); + $database->execute(); + + $NoteId = $database->lastInsertId(); + + $database->query("INSERT INTO tag_notes (tag_name, note_id) VALUES (:tag_name, :note_id)"); + $database->bind(':tag_name', $tag_name); + $database->bind(':note_id', $NoteId); + $database->execute(); + + } + } +} diff --git a/app/core/Database.php b/app/core/Database.php new file mode 100644 index 0000000000000000000000000000000000000000..3a7431e9c341de50c94dcf04e61263c50fa3c613 --- /dev/null +++ b/app/core/Database.php @@ -0,0 +1,104 @@ +<?php + +require_once __DIR__ . '/Tables.php'; + +class Database{ + private $host = DB_HOST; + private $user = DB_USER; + private $password = DB_PASSWORD; + private $port = DB_PORT; + private $name = DB_NAME; + private $db_connection; + private $statement; + + public function __construct(){ + $dsn = "pgsql:host=" . $this->host . ";dbname=" . $this->name . ";user=" . $this->user . ";password=" . $this->password . ";port=" . $this->port; + + try{ + $this->db_connection = new PDO($dsn); + } catch (PDOException $e){ + echo($e->getMessage()); + } + + try{ + $this->db_connection->exec((Tables::USER_TABLE)); + $this->db_connection->exec((Tables::NOTE_TABLE)); + $this->db_connection->exec((Tables::REMINDER_TABLE)); + $this->db_connection->exec((Tables::TAG_TABLE)); + $this->db_connection->exec((Tables::TAG_NOTE_TABLE)); + $this->db_connection->exec((Tables::CATEGORY_REMINDER_TABLE)); + + } catch(PDOException $e){ + echo($e->getMessage()); + } + } + + public function query($query){ + try{ + $this->statement = $this->db_connection->prepare(($query)); + } catch (PDOException $e){ + echo($e->getMessage()); + } + } + + public function bind($param, $value, $type = null){ + try{ + if(is_null($type)){ + if(is_int($value)){ + $type = PDO::PARAM_INT; + } else if (is_bool($value)){ + $type = PDO::PARAM_BOOL; + } else if (is_bool($value)){ + $type = PDO::PARAM_NULL; + } else { + $type = PDO::PARAM_STR; + } + } + $this->statement->bindValue($param, $value, $type); + } catch(PDOException $e){ + echo($e->getMessage()); + } + } + + public function execute(){ + try{ + $this->statement->execute(); + } catch(PDOException $e){ + echo($e->getMessage()); + } + } + + public function fetch(){ + try{ + $this->execute(); + return $this->statement->fetch(PDO::FETCH_OBJ); + } catch(PDOException $e){ + echo($e->getMessage()); + } + } + + public function fetchAll(){ + try{ + $this->execute(); + return $this->statement->fetchAll(PDO::FETCH_OBJ); + } catch(PDOException $e){ + echo($e->getMessage()); + } + } + + public function rowCount(){ + try{ + return $this->statement->rowCount(); + } catch(PDOException $e){ + echo($e->getMessage()); + } + } + + public function lastInsertId() { + try{ + return $this->db_connection->lastInsertId(); + } catch(PDOException $e){ + echo($e->getMessage()); + } + } +} diff --git a/app/core/Tables.php b/app/core/Tables.php new file mode 100644 index 0000000000000000000000000000000000000000..0c44a77c03a9dbdceca4cd01a2b80d9d88a0bab9 --- /dev/null +++ b/app/core/Tables.php @@ -0,0 +1,56 @@ +<?php + +class Tables{ + public const USER_TABLE = + "CREATE TABLE IF NOT EXISTS users ( + user_id SERIAL PRIMARY KEY, + username VARCHAR(256) UNIQUE NOT NULL, + email VARCHAR(256) UNIQUE NOT NULL, + password VARCHAR(256) NOT NULL, + is_admin BOOLEAN NOT NULL + );"; + + public const NOTE_TABLE = + "CREATE TABLE IF NOT EXISTS notes ( + note_id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + title VARCHAR(256) NOT NULL, + content VARCHAR(1024) NOT NULL, + image_path VARCHAR(256) NOT NULL, + video_path VARCHAR(256) NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (user_id) ON UPDATE CASCADE ON DELETE CASCADE + );"; + + public const REMINDER_TABLE = + "CREATE TABLE IF NOT EXISTS reminders ( + reminder_id SERIAL PRIMARY KEY, + user_id INT NOT NULL, + title VARCHAR(256) NOT NULL, + deadline TIMESTAMP NOT NULL, + tasks VARCHAR(256) NOT NULL, + FOREIGN KEY (user_id) REFERENCES users (user_id) ON UPDATE CASCADE ON DELETE CASCADE + );"; + + public const TAG_TABLE = + "CREATE TABLE IF NOT EXISTS tags ( + tag_name VARCHAR(256) PRIMARY KEY + );"; + + public const TAG_NOTE_TABLE = + "CREATE TABLE IF NOT EXISTS tag_notes ( + tag_name VARCHAR(256), + note_id INT, + PRIMARY KEY (tag_name, note_id), + FOREIGN KEY (tag_name) REFERENCES tags (tag_name) ON UPDATE CASCADE ON DELETE CASCADE, + FOREIGN KEY (note_id) REFERENCES notes (note_id) ON UPDATE CASCADE ON DELETE CASCADE + );"; + + public const CATEGORY_REMINDER_TABLE = + "CREATE TABLE IF NOT EXISTS category_reminders ( + category_name VARCHAR(256), + reminder_id INT, + PRIMARY KEY (category_name, reminder_id), + FOREIGN KEY (category_name) REFERENCES tags (tag_name) ON UPDATE CASCADE ON DELETE CASCADE, + FOREIGN KEY (reminder_id) REFERENCES reminders (reminder_id) ON UPDATE CASCADE ON DELETE CASCADE + );"; +} \ No newline at end of file diff --git a/app/init.php b/app/init.php new file mode 100644 index 0000000000000000000000000000000000000000..1723865c929650d7618e41a12e360fe600f353b6 --- /dev/null +++ b/app/init.php @@ -0,0 +1,7 @@ +<?php + +require_once 'core/App.php'; +require_once 'core/Controller.php'; +require_once 'core/Database.php'; +require_once 'core/Tables.php'; +require_once 'config/config.php'; \ No newline at end of file diff --git a/app/models/CategoryRemindersModel.php b/app/models/CategoryRemindersModel.php new file mode 100644 index 0000000000000000000000000000000000000000..0554b62a81888d38aeaaedf7d837a70e63f6171f --- /dev/null +++ b/app/models/CategoryRemindersModel.php @@ -0,0 +1,62 @@ +<?php +require_once __DIR__ .'/../../config/config.php'; +require_once __DIR__ .'/../../app/core/Database.php'; +class CategoryRemindersModel +{ + private $table = 'category_reminders'; + private $db; + + public function __construct() + { + $this->db = new Database; + } + + public function getAllCategoryReminders() + { + $this->db->query('SELECT * FROM ' . $this->table); + return $this->db->fetchAll(); + } + public function getReminderByCategory($category_name) + { + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE category_name = :category_name'); + $this->db->bind('category_name', $category_name); + return $this->db->fetchAll(); + } + + public function insertReminderCategory($data) + { + $this->db->query('INSERT INTO ' . $this->table . ' (category_name, reminder_id) VALUES (:category_name, :reminder_id)'); + + $this->db->bind('category_name', $data['category']); + $this->db->bind('reminder_id', $data['reminder_id']); + + $this->db->execute(); + + return $this->db->rowCount(); + } + + public function deleteReminderCategory($data) + { + $this->db->query('DELETE FROM ' . $this->table . ' WHERE reminder_id = :reminder_id AND category_name = :category_name'); + + $this->db->bind('category_name', $data['category']); + $this->db->bind('reminder_id', $data['reminder_id']); + + $this->db->execute(); + + return $this->db->rowCount(); + } + public function updateReminderCategory($data) + { + $this->db->query('UPDATE ' . $this->table . ' SET category_name = :category_name WHERE reminder_id = :reminder_id'); + + $this->db->bind('category_name', $data['category']); + $this->db->bind('reminder_id', $data['reminder_id']); + + $this->db->execute(); + + return $this->db->rowCount(); + } + + +} \ No newline at end of file diff --git a/app/models/ReminderModel.php b/app/models/ReminderModel.php new file mode 100644 index 0000000000000000000000000000000000000000..8ed2268697602ee5567b06a7d499fa3660f80a3f --- /dev/null +++ b/app/models/ReminderModel.php @@ -0,0 +1,114 @@ +<?php +require_once __DIR__ .'/../../config/config.php'; +require_once __DIR__ .'/../../app/core/Database.php'; +require_once __DIR__ . '/CategoryRemindersModel.php'; +require_once __DIR__ .'/../../app/core/DataSeeder.php'; +class ReminderModel +{ + private $table = 'reminders'; + private $db; + private $CategoryRemindersModel; + + public function __construct() + { + + $this->db = new Database; + $this->CategoryRemindersModel = new CategoryRemindersModel; + + } + + public function getAllReminders() + { + $this->db->query('SELECT * FROM ' . $this->table); + return $this->db->fetchAll(); + } + + public function getReminderById($id) + { + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE reminder_id = :id'); + $this->db->bind('id', $id); + return $this->db->fetch(); + } + public function getReminderByUserId($user_id) + { + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE user_id = :id'); + $this->db->bind('id', $user_id); + + return $this->db->fetchAll(); + } + public function insertReminder($data) + { + $this->db->query('INSERT INTO ' . $this->table . ' (user_id, title, deadline, tasks) VALUES (:user_id, :title, :deadline, :tasks)'); + + $this->db->bind('user_id', $data['user_id']); + $this->db->bind('title', $data['title']); + $this->db->bind('deadline', $data['deadline']); + $this->db->bind('tasks', $data['tasks']); + + $this->db->execute(); + + $lastInsertedId = $this->db->lastInsertId(); + + $data['reminder_id'] = $lastInsertedId; + + if (isset($data['category']) && !empty($data['category']) && isset($data['reminder_id']) && !empty($data['reminder_id'])) { + $this->CategoryRemindersModel->insertReminderCategory($data); + } + + return $this->db->rowCount(); + } + + public function updateReminder($data) + { + $this->db->query('UPDATE ' . $this->table . ' SET user_id = :user_id, title = :title, deadline = :deadline, tasks = :tasks WHERE reminder_id = :reminder_id'); + + $this->db->bind('user_id', $data['user_id']); + $this->db->bind('title', $data['title']); + $this->db->bind('deadline', $data['deadline']); + $this->db->bind('tasks', $data['tasks']); + $this->db->bind('reminder_id', $data['reminder_id']); + + $this->db->execute(); + if (isset($data['category']) && !empty($data['category'])) { + $this->CategoryRemindersModel->updateReminderCategory($data); + } + + return $this->db->rowCount(); + } + + public function deleteReminder($data) + { + if (isset($data['category']) && !empty($data['category'])) { + $this->CategoryRemindersModel->deleteReminderCategory($data); + } + + $this->db->query('DELETE FROM ' . $this->table . ' WHERE reminder_id = :reminder_id'); + + $this->db->bind('reminder_id', $data['reminder_id']); + + $this->db->execute(); + + return $this->db->rowCount(); + } + + public function getCategoryByReminderId($id) + { + $this->db->query('SELECT * FROM category_reminders WHERE reminder_id = :id'); + + $this->db->bind('id', $id); + return $this->db->fetch(); + } + public function findReminders($data) + { + $title = '%' . $data['title'] . '%'; + + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE title LIKE :title'); + $this->db->bind('title', $title); + + return $this->db->fetchAll(); + } + + public function filterReminders($data){ + $this->CategoryRemindersModel->getReminderByCategory($data); + } +} \ No newline at end of file diff --git a/app/models/TagsModel.php b/app/models/TagsModel.php new file mode 100644 index 0000000000000000000000000000000000000000..5e70e185ae593d5eb4f5f5427815d7b9b24e1ec2 --- /dev/null +++ b/app/models/TagsModel.php @@ -0,0 +1,20 @@ +<?php +require_once __DIR__ .'/../../config/config.php'; +require_once __DIR__ .'/../../app/core/Database.php'; +class TagsModel +{ + private $table = 'tags'; + private $db; + + public function __construct() + { + $this->db = new Database; + } + + public function getAllTags() + { + $this->db->query('SELECT * FROM ' . $this->table); + return $this->db->fetchAll(); + } + +} \ No newline at end of file diff --git a/app/models/notes.php b/app/models/notes.php new file mode 100644 index 0000000000000000000000000000000000000000..d56418a12f3981a2dd172e0a61c7fc38d2147e05 --- /dev/null +++ b/app/models/notes.php @@ -0,0 +1,431 @@ +<?php + +require_once __DIR__ .'/../../config/config.php'; +require_once __DIR__ .'/../../app/core/Database.php'; + +class NoteModel +{ + private $table = 'notes'; + private $db; + + public function __construct() + { + $this->db = new Database; + } + + public function getAllNotes() + { + $this->db->query('SELECT * FROM ' . $this->table); + return $this->db->fetchAll(); + } + + // public function getLatestNoteId($user_id = "") + // { + // $this->db->query('SELECT note_id FROM ' . $this->table . ' ORDER BY note_id DESC LIMIT 1'); + // return $this->db->fetch(); + // } + + public function getLatestNoteId($user_id = "") + { + $query = 'SELECT note_id FROM ' . $this->table; + if($user_id != "") { + $query .= ' WHERE user_id = :user_id'; + } + + $query .= ' ORDER BY note_id DESC LIMIT 1'; + + $this->db->query($query); + if($user_id != "") { + $this->db->bind('user_id', $user_id); + } + + return $this->db->fetch(); + } + + + // public function getCountNotes() + // { + // $this->db->query('SELECT COUNT(note_id) as num_note FROM tag_notes'); + + // return $this->db->fetch(); + // } + + public function getCountNotes($data) { + $query = "SELECT COUNT(n.note_id) num_note FROM " . $this->table . " n JOIN tag_notes t ON n.note_id = t.note_id"; + $where = false; + $binds = array(); + + if (isset($data['user_id'])) { + $query .= " WHERE n.user_id = :user_id"; + $binds['user_id'] = $data['user_id']; + } + + if (isset($data['title'])) { + $query .= " AND LOWER(n.title) LIKE :title"; + // $query .= " WHERE LOWER(n.title) LIKE :title"; + $binds['title'] = '%' . strtolower($data['title']) . '%'; + $where = true; + } + + // Memproses tags jika disediakan + if (isset($data['tags']) && !empty($data['tags'])) { + if ($where) { + $query .= " AND t.tag_name IN ("; + + // Menggunakan placeholder untuk setiap tag + $tags = explode(',', $data['tags']); + foreach ($tags as $index => $tag) { + $query .= ":tag" . $index . ","; + $binds['tag' . $index] = trim($tag); + } + + // Menghapus koma ekstra dan menutup kurung + $query = rtrim($query, ',') . ")"; + } else { + // Jika tidak ada kondisi WHERE sebelumnya, gunakan WHERE + $query .= " WHERE t.tag_name IN ("; + + // Menggunakan placeholder untuk setiap tag + $tags = explode(',', $data['tags']); + foreach ($tags as $index => $tag) { + $query .= ":tag" . $index . ","; + $binds['tag' . $index] = trim($tag); + } + + // Menghapus koma ekstra dan menutup kurung + $query = rtrim($query, ',') . ")"; + } + } + + // Persiapan prepared statement + $this->db->query($query); + + // Bind parameters + foreach ($binds as $key => $value) { + $this->db->bind($key, $value); + } + + // Eksekusi prepared statement + $this->db->execute(); + + return $this->db->fetch(); + } + + public function getUserByNoteId($noteId) + { + $this->db->query('SELECT user_id FROM ' . $this->table . ' WHERE note_id = :id'); + $this->db->bind('id', $noteId); + + return $this->db->fetch(); + } + + public function getNoteById($id) + { + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE note_id = :id'); + $this->db->bind('id', $id); + + return $this->db->fetch(); + } + + public function getNotesByUserId($id) + { + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE user_id = :id'); + $this->db->bind('id', $id); + + return $this->db->fetchAll(); + } + + public function getNotesAndTagsByUserId($id) + { + $this->db->query('SELECT 1 FROM ' . $this->table . ' n JOIN tag_notes t ON n.note_id = t.note_id WHERE user_id = :id'); + $this->db->bind('id', $id); + + return $this->db->fetchAll(); + } + + public function getNotesByUserIdAndPage($id, $page) { + $resultsPerPage = 4; // Jumlah hasil per halaman + $offset = ($page - 1) * $resultsPerPage; // Hitung offset berdasarkan halaman + + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE user_id = :id LIMIT :limit OFFSET :offset'); + + $this->db->bind('id', $id); + $this->db->bind('limit', $resultsPerPage); + $this->db->bind('offset', $offset); + + $this->db->execute(); + return $this->db->fetchAll(); + } + + public function insertNote($data){ + // Kueri SQL dengan prepared statement + $this->db->query('INSERT INTO ' . $this->table . ' (title, content, user_id, video_path, image_path) VALUES (:title, :content, :user_id, :video_path, :image_path)'); + + // Bind parameter dengan nilai yang sesuai + $this->db->bind('title', $data['title']); + $this->db->bind('content', $data['content']); + $this->db->bind('user_id', $data['user_id']); + $this->db->bind('video_path', $data['video_path']); + $this->db->bind('image_path', $data['image_path']); + + // Eksekusi prepared statement + $this->db->execute(); + + // Mengembalikan jumlah baris yang terpengaruh (biasanya 1 jika berhasil) + return $this->db->rowCount(); + } + + public function editNote($data) { + // Kueri SQL dengan prepared statement + $this->db->query('UPDATE ' . $this->table . ' SET title = :title, content = :content, video_path = :video_path, image_path = :image_path WHERE note_id = :id'); + + // Bind parameter dengan nilai yang sesuai + $this->db->bind('title', $data->title); + $this->db->bind('content', $data->content); + $this->db->bind('video_path', $data->video_path); + $this->db->bind('image_path', $data->image_path); + $this->db->bind('id', $data->note_id); + + // Eksekusi prepared statement + $this->db->execute(); + + // Mengembalikan jumlah baris yang terpengaruh (biasanya 1 jika berhasil) + // return $stmt->rowCount(); + return true; + } + + public function deleteNote($id) { + $this->db->query('DELETE FROM ' . $this->table . ' WHERE note_id = :id'); + + $this->db->bind('id', $id); + return $this->db->fetch(); + } + + + // public function findNotes($data) { + // $query = "SELECT * FROM " . $this->table . " n JOIN tag_notes t ON n.note_id = t.note_id"; + // $where = false; + // $binds = array(); + + // if (isset($data['user_id'])) { + // $query .= " WHERE n.user_id = :user_id"; + // $binds['user_id'] = $data['user_id']; + // } + + // if (isset($data['title'])) { + // if (isset($data['user_id'])) { + // $query .= " AND LOWER(n.title) LIKE :title"; + // } else { + // $query .= " WHERE LOWER(n.title) LIKE :title"; + // } + + // $binds['title'] = '%' . strtolower($data['title']) . '%'; + // $where = true; + // } + + // // Memproses tags jika disediakan + // if (isset($data['tags']) && !empty($data['tags'])) { + // if ($where) { + // $query .= " AND t.tag_name IN ("; + + // // Menggunakan placeholder untuk setiap tag + // $tags = explode(',', $data['tags']); + // foreach ($tags as $index => $tag) { + // $query .= ":tag" . $index . ","; + // $binds['tag' . $index] = trim($tag); + // } + + // // Menghapus koma ekstra dan menutup kurung + // $query = rtrim($query, ',') . ")"; + // } else { + // // Jika tidak ada kondisi WHERE sebelumnya, gunakan WHERE + // $query .= " WHERE t.tag_name IN ("; + + // // Menggunakan placeholder untuk setiap tag + // $tags = explode(',', $data['tags']); + // foreach ($tags as $index => $tag) { + // $query .= ":tag" . $index . ","; + // $binds['tag' . $index] = trim($tag); + // } + + // // Menghapus koma ekstra dan menutup kurung + // $query = rtrim($query, ',') . ")"; + // } + // } + + // $query .= " ORDER BY n.note_id"; + // if (isset($data['sort'])) { + // if ($data['sort'] == 1) { + // $query .= " ASC"; + // } + // } else { + // $query .= " DESC"; + // } + + // $query .= " LIMIT 4"; + + // // Menambahkan OFFSET jika halaman diberikan + // if (isset($data['page'])) { + // $query .= " OFFSET :offset"; + // $binds['offset'] = $data['limit'] * ($data['page'] - 1); + // } else { + // $query .= " OFFSET 0"; + // } + + // // Persiapan prepared statement + // $this->db->query($query); + + // // Bind parameters + // foreach ($binds as $key => $value) { + // $this->db->bind($key, $value); + // } + + // // Eksekusi prepared statement + // $this->db->execute(); + + // return $this->db->fetchAll(); + // } + + public function findNotes($data) { + $query = "SELECT * FROM " . $this->table . " n JOIN tag_notes t ON n.note_id = t.note_id"; + $where = false; + $binds = array(); + + if (isset($data['user_id'])) { + $query .= " WHERE n.user_id = :user_id"; + $binds['user_id'] = $data['user_id']; + $where = true; + } + + if (isset($data['title'])) { + if ($where) { + $query .= " AND LOWER(n.title) LIKE :title"; + } else { + $query .= " WHERE LOWER(n.title) LIKE :title"; + $where = true; + } + + $binds['title'] = '%' . strtolower($data['title']) . '%'; + } + + // Memproses tags jika disediakan + if (isset($data['tags']) && !empty($data['tags'])) { + if ($where) { + $query .= " AND t.tag_name IN ("; + } else { + $query .= " WHERE t.tag_name IN ("; + $where = true; + } + + // Menggunakan placeholder untuk setiap tag + $tags = explode(',', $data['tags']); + foreach ($tags as $index => $tag) { + $query .= ":tag" . $index . ","; + $binds['tag' . $index] = trim($tag); + } + + // Menghapus koma ekstra dan menutup kurung + $query = rtrim($query, ',') . ")"; + } + + $query .= " ORDER BY n.note_id"; + if (isset($data['sort'])) { + if ($data['sort'] == 1) { + $query .= " ASC"; + } + } else { + $query .= " DESC"; + } + + $query .= " LIMIT 4"; + + // Menambahkan OFFSET jika halaman diberikan + if (isset($data['page'])) { + $query .= " OFFSET :offset"; + $binds['offset'] = $data['limit'] * ($data['page'] - 1); + } else { + $query .= " OFFSET 0"; + } + + // Persiapan prepared statement + $this->db->query($query); + + // Bind parameters + foreach ($binds as $key => $value) { + $this->db->bind($key, $value); + } + + // Eksekusi prepared statement + $this->db->execute(); + + return $this->db->fetchAll(); + } + + public function getTagsByNoteId($id) + { + $this->db->query('SELECT * FROM tag_notes WHERE note_id = :id'); + $this->db->bind('id', $id); + return $this->db->fetchAll(); + } + + public function insertTags($note_id, $tags) { + // Kueri SQL dengan prepared statement + $sql = 'INSERT INTO tag_notes (note_id, tag_name) VALUES '; + + // Membuat array placeholder untuk setiap pasangan note_id dan tag_name + $placeholders = array(); + $binds = array(); + + foreach ($tags as $index => $tag) { + $placeholders[] = '(:note_id, :tag_name' . $index . ')'; + $binds['tag_name' . $index] = $tag; + } + + // Gabungkan semua placeholder dengan koma untuk membuat satu baris VALUES + $sql .= implode(', ', $placeholders); + + $this->db->query($sql); + + // Bind parameter dengan nilai yang sesuai + $this->db->bind('note_id', $note_id); + return ($sql); + + foreach ($binds as $key => $value) { + $this->db->bind($key, $value, PDO::PARAM_STR); + } + + // Eksekusi prepared statement + $this->db->execute(); + + // Mengembalikan jumlah baris yang terpengaruh (jumlah tag yang berhasil disisipkan) + return count($tags); + } + + public function insertTag($note_id, $tags) { + // Kueri SQL dengan prepared statement + $sql = 'INSERT INTO tag_notes (note_id, tag_name) VALUES (:note_id, :tag_name)'; + $this->db->query($sql); + // Bind parameter dengan nilai yang sesuai + $this->db->bind('note_id', $note_id); + $this->db->bind('tag_name', $tags, PDO::PARAM_STR); + + // Eksekusi prepared statement + $this->db->execute(); + + return true; + } + + public function deleteTagsByNoteId($noteId) { + // Kueri SQL dengan prepared statement untuk menghapus tag berdasarkan note_id + $this->db->query('DELETE FROM tag_notes WHERE note_id = :noteId'); + + // Bind parameter dengan nilai yang sesuai + $this->db->bind(':noteId', $noteId, PDO::PARAM_INT); + + // Eksekusi prepared statement + $this->db->execute(); + + // Mengembalikan jumlah baris yang terpengaruh (jumlah tag yang dihapus) + return $this->db->rowCount(); + } +} diff --git a/app/models/user.php b/app/models/user.php new file mode 100644 index 0000000000000000000000000000000000000000..e4c6a0ed1bc8a432db66b4f0b4883c35e8274436 --- /dev/null +++ b/app/models/user.php @@ -0,0 +1,94 @@ +<?php +require_once __DIR__ .'/../../config/config.php'; +require_once __DIR__ .'/../../app/core/Database.php'; +require_once __DIR__ .'/../../app/core/DataSeeder.php'; +class UserModel { + private $table = 'users'; + private $db; + + public function __construct() + { + $this->db = new Database; + + $seedingStatusFile = __DIR__ . '/seeding.lock'; + + if (!file_exists($seedingStatusFile)) { + if (touch($seedingStatusFile)) { + $dataSeeder = new DataSeeder(); + $dataSeeder->seedDatas(); + } else {} + } else {} + } + + public function getAllUser(){ + $this->db->query('SELECT * FROM users'); + return $this->db->fetchAll(); + } + + public function getUserTableInfos(){ + $this->db->query('SELECT user_id, username, email FROM users ORDER BY user_id'); + return $this->db->fetchAll(); + } + + public function getUserById($id) + { + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE user_id = :id'); + $this->db->bind('id', $id); + return $this->db->fetch(); + } + + public function getUserByEmail($email) + { + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE email = :email'); + $this->db->bind('email', $email); + return $this->db->fetch(); + } + + public function getUserByUsername($username) + { + $this->db->query('SELECT * FROM ' . $this->table . ' WHERE username = :username'); + $this->db->bind('username', $username); + return $this->db->fetch(); + } + + public function register($data) + { + $this->db->query('INSERT INTO ' . $this->table . ' (username, email, password, is_admin) VALUES (:username, :email, :password, false)'); + $this->db->bind('username', $data['username']); + $this->db->bind('email', $data['email']); + $this->db->bind('password', password_hash($data['password'], PASSWORD_DEFAULT)); + $this->db->execute(); + return $this->db->rowCount(); + } + + public function deleteUserById($userId) { + // Kueri SQL dengan prepared statement untuk menghapus tag berdasarkan note_id + $this->db->query('DELETE FROM ' . $this->table . ' WHERE user_id = :userId'); + + // Bind parameter dengan nilai yang sesuai + $this->db->bind(':userId', $userId, PDO::PARAM_INT); + + // Eksekusi prepared statement + $this->db->execute(); + + // Mengembalikan jumlah baris yang terpengaruh (jumlah tag yang dihapus) + return $this->db->rowCount(); + } + + public function updateUser($data) { + // Kueri SQL dengan prepared statement untuk mengupdate user berdasarkan user_id + $query = 'UPDATE ' . $this->table . ' SET email = :email, password = :password WHERE user_id = :user_id'; + $this->db->query($query); + + // Bind parameter dengan nilai yang sesuai + $this->db->bind(':user_id', $data['user_id'], PDO::PARAM_INT); + $this->db->bind(':email', $data['email'], PDO::PARAM_STR); + $this->db->bind(':password', password_hash($data['password'], PASSWORD_DEFAULT), PDO::PARAM_STR); + + // Eksekusi prepared statement + $this->db->execute(); + + // Mengembalikan jumlah baris yang terpengaruh (jumlah user yang diupdate) + return $this->db->rowCount(); + } +} diff --git a/app/views/Home/index.php b/app/views/Home/index.php new file mode 100644 index 0000000000000000000000000000000000000000..3dca1005f8dc48039aab481ec268bcba7e1d7595 --- /dev/null +++ b/app/views/Home/index.php @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Home</title> + <link rel="stylesheet" href="../../../public/css/home.css" /> +</head> +<body> + <div class="navbar"> + <?php + include(__DIR__ . '/../navbar/navbar.php'); + ?> + </div> + <div class="flex-column text al"> + <div class="welcome"> + <?php + $name = $data['user_info']; + echo "Welcome, $name"; + ?> + </div> + <div class="info flex-row align-center"> + <div class="info-notes box flex-column center"> + <?php + $total_notes = $data['count_notes'] + ?> + <div class="judul_notes"> + Jumlah Notes + </div> + <div class="jumlah_notes number"> + <?php + echo "$total_notes"; + ?> + </div> + </div> + <div class="info-reminders box flex-column center"> + <?php + $total_reminders = $data['count_reminders'] + ?> + <div class="judul_reminders"> + Jumlah Reminders + </div> + <div class="jumlah_reminders number"> + <?php + echo "$total_reminders"; + ?> + </div> + </div> + </div> + </div> + +</body> +</html> \ No newline at end of file diff --git a/app/views/Login/index.php b/app/views/Login/index.php new file mode 100644 index 0000000000000000000000000000000000000000..f1d42c7a79c0586154a685844d17692386a4b147 --- /dev/null +++ b/app/views/Login/index.php @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>signin</title> + <link rel="stylesheet" href="../../../public/css/signin.css" /> +</head> +<body> + <div class="container"> + <h1 class="form-title">Sign in</h1> + <form action="/app/api/user/login.php" method="post"> + <div class="main-user-info"> + <div class="user-input-box"> + <input + id="email" + name="email" + placeholder="Enter Email" + type="email"/> + </div> + <div class="user-input-box"> + <input + id="password" + name="password" + placeholder="Enter Password" + type="password"/> + </div> + <div class="form-submit-btn"> + <input type="submit" value="Sign in"> + </div> + <div class="signup">Don't have an account? + <div onmouseover='setCursorPointer(this)' onmouseout='resetCursor(this)' onclick="window.location.href='/?register'" + class="signup-text" + > + Sign Up + </div> + </div> + </form> + </div> + <script src="../../../public/js/navbar.js"></script> + </body> +</html> \ No newline at end of file diff --git a/app/views/Register/index.php b/app/views/Register/index.php new file mode 100644 index 0000000000000000000000000000000000000000..ff98cde3ea4dfeff4e9cb55a764975af2e82fd24 --- /dev/null +++ b/app/views/Register/index.php @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>signup</title> + <link rel="stylesheet" href="../../../public/css/signup.css" /> +</head> +<body> + <div class="container"> + <h1 class="form-title">Sign up</h1> + <form action="/app/api/user/register.php" method="post"> + <div class="main-user-info"> + <div class="user-input-box"> + <input + id="email" + type="email" + name="email" + placeholder="Enter Email"/> + </div> + <div class="user-input-box"> + <input + id="username" + name="username" + placeholder="Enter Username"/> + </div> + <div class="user-input-box"> + <input + id="password" + name="password" + type="password" + placeholder="Enter Password"/> + </div> + <div class="form-submit-btn"> + <input type="submit" value="Sign up"></html> + - </form> + </div> + </body> \ No newline at end of file diff --git a/app/views/admin/index.php b/app/views/admin/index.php new file mode 100644 index 0000000000000000000000000000000000000000..0869065c51b61846e3e1a0b6c157e7630caf0000 --- /dev/null +++ b/app/views/admin/index.php @@ -0,0 +1,67 @@ +<?php + include_once 'app/core/Database.php'; +?> + +<!DOCTYPE html> +<html lang="en"> + + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title> Admin Page </title> + <link rel="stylesheet" href="../../../public/css/admin.css" /> + <link + rel="stylesheet" + href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" + integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" + crossorigin="anonymous" + referrerpolicy="no-referrer" + /> + <script src="../../../public/js/admin.js"></script> + </head> + + <body> + <div class="navbar"> + <?php + include(__DIR__ . '/../navbar/navbar.php'); + ?> + </div> + <script> + fillUserTable(); + </script> + + <div class="main-container"> + <div class="top-container"> + User Notes and Reminders + </div> + + <div class="user-table-container"> + <div class="table-header-row-container"> + <div class="user-id"> + User ID + </div> + <div class="username"> + Username + </div> + <div class="email"> + Email + </div> + <div class="notes"> + Notes + </div> + <div class="reminder"> + Reminders + </div> + <div class="edit"> + Edit + </div> + <div class="delete"> + Delete + </div> + </div> + <div class="row-container"> + </div> + </div> + </div> + </body> +</html> \ No newline at end of file diff --git a/app/views/error/index.php b/app/views/error/index.php new file mode 100644 index 0000000000000000000000000000000000000000..a02e282b88805b1d103f9b8fa5f32f2eba505f0f --- /dev/null +++ b/app/views/error/index.php @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Error</title> + <link rel="stylesheet" href="../../../public/css/error.css" /> +</head> +<body> + <div class="flex-column align-center"> + <h1 class="text align-center">404</h1> + <p class="text align-center">Oops, page not found</p> + <button class="button align-center" type="button"><a class="text" href="/?Home">Back to Home</a></button> + </div> + +</body> +</html> \ No newline at end of file diff --git a/app/views/navbar/navbar.php b/app/views/navbar/navbar.php new file mode 100644 index 0000000000000000000000000000000000000000..bb5965b61c737b37de7c9ca7f50345597c9fce72 --- /dev/null +++ b/app/views/navbar/navbar.php @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" href="../../../public/css/navbar.css" /> + </head> + <body> + <div class="navbar"> + <div class="logo">My web</div> + <div class="navbar-div"> + <?php + if(isset($data['is_admin'])){ + if ($data['is_admin']==1){ + echo"<a class='admin navbar-text' href='/?admin' onmouseover='setCursorPointer(this)' onmouseout='resetCursor(this)'>Admin</a>"; + } + } + echo" + <a class='home navbar-text' href='/?home' onmouseover='setCursorPointer(this)' onmouseout='resetCursor(this)'>Home</a> + <a class='reminders navbar-text' href='/?reminder' onmouseover='setCursorPointer(this)' onmouseout='resetCursor(this)'>Reminders</a> + <a class='notes navbar-text' href='/?notes' onmouseover='setCursorPointer(this)' onmouseout='resetCursor(this)'>Notes</a> + <div class='dropdown'> + <img class='profile' onclick='toggleDropdown()'' onmouseover='setCursorPointer(this)' onmouseout='resetCursor(this)' src='../../../public/images/profile.png' /> + <div class='dropdown-content'> + <a class='dropdown-content-2' href='/app/api/user/logout.php'>Sign Out</a> + </div> + </div> + <div class='dropdown-responsif'> + <i class='fas fa-bars more' id='moreIcon' onclick='toggleDropdown()'' onmouseover='setCursorPointer(this)' onmouseout='resetCursor(this)'></i> + <div class='dropdown-content'>"; + + if(isset($data['is_admin'])){ + if ($data['is_admin']==1){ + echo"<a class='dropdown-content-0' href='/?admin' onmouseover='setCursorPointer(this)' onmouseout='resetCursor(this)'>Admin</a>"; + } + } + echo" + <a class='dropdown-content-0' href='/?home'>Home</a> + <a class='dropdown-content-0' href='/?reminder'>Reminders</a> + <a class='dropdown-content-0' href='/?notes'>Notes</a> + <a class='dropdown-content-0' href='/app/api/user/logout.php'>Sign Out</a> + </div> + </div>"; + ?> + </div> + </div> + <script src="../../../public/js/navbar.js"></script> + </body> +</html> diff --git a/app/views/notes/addnote.php b/app/views/notes/addnote.php new file mode 100644 index 0000000000000000000000000000000000000000..9ca8c3cedd252614709ad2b29a988d51d19b0b45 --- /dev/null +++ b/app/views/notes/addnote.php @@ -0,0 +1,172 @@ +<?php + include_once 'app/core/Database.php'; +?> + +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title> NotesApp </title> + <link rel="stylesheet" href="../../../public/css/addnote.css" /> + <!-- <link rel="stylesheet" href="../../../public/css/styles.css" /> --> + <link + rel="stylesheet" + href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" + integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" + crossorigin="anonymous" + referrerpolicy="no-referrer" + /> + <!-- <script src="../../../public/js/utility.js"></script> --> + <script src="../../public/js/noteupload.js"></script> + <!-- <script src="../../../public/js/navbar.js"></script> --> + </head> + + <body> + <div class="navbar"> + <?php + include(__DIR__ . '/../navbar/navbar.php'); + ?> + </div> + <div class="main-container"> + <div class="top-buttons"> + <div class="back-button" onclick=backSave() onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"> + <button class="search-button" onclick=backSave() onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"> + <i class="fa-solid fa-chevron-left"></i> + </button> + <div class="back"> Back </div> + </div> + </div> + <div class="new-note-title"> + New note + </div> + <div class="note-data-cont"> + <div class="note-text-cont"> + <div class="text-input-cont"> + <input type="text" name="text-input" class="text-input" placeholder="Note title"> + </div> + + <div class="textarea-input-cont"> + <textarea name="textarea-input" class="textarea-input" placeholder="Note content"></textarea> + </div> + </div> + <div class="tags-media-cont"> + <div class="tags-section-cont"> + <div class="tags-button-cont"> + <button class="tags-button" onclick=toggleChooseTag() onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"> + <i class="fa-solid fa-tags"></i> + Tags + </button> + </div> + <div id="tags-cont"> + </div> + <div class="tags-list-container" style="display: none;"> + <?php + $tags = $data['allTagsData']; + foreach ($tags as $tag) { + if ($tag->tag_name != 'All') { + echo '<div class="tag" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick="chooseTag(\'' . $tag->tag_name . '\')">' . $tag->tag_name . '</div>'; + } + } + ?> + </div> + </div> + + <div class="media-section-cont"> + <div class="thumbnail-cont"> + <!-- Input Unggah Thumbnail --> + <div class="upload-thumbnail"> + <input type="file" id="thumbnail-upload" accept="image/*" hidden> + <label for="thumbnail-upload" class="upload-button"> + <div class="thumbnail-upload-button"> + <i class="fa-solid fa-upload"></i> Upload Thumbnail + </div> + </label> + </div> + + <!-- Tampilkan Thumbnail yang Diunggah --> + <div class="note-thumbnail"> + <img src="#" alt="Thumbnail" id="uploaded-thumbnail-img"> + <div id="uploaded-thumbnail-div"> + Note Thumbnail + </div> + </div> + </div> + + <div class="video-cont"> + <!-- Input Unggah Video --> + <div class="upload-video"> + <input type="file" id="video-upload" accept="video/*" hidden> + <label for="video-upload"> + <div class="video-upload-button"> + <i class="fa-solid fa-upload"></i> Upload Video + </div> + </label> + </div> + + <!-- Tampilkan Video yang Diunggah --> + <div class="note-video"> + <video controls width="100%" height="auto" id="uploaded-video-vid-cont"> + <source src="#" id="uploaded-video-vid" type="video/mp4"> + </video> + + <div id="uploaded-video-div"> + Note Video + </div> + </div> + </div> + </div> + + <div class="add-note" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick="saveNote(<?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>)" > + <!-- <div class="add-note" onclick="saveNote()"> --> + <i class="fa-solid fa-plus"></i> + Add Note + </div> + </div> + + </div> + <div> + <script> + // Unggah Video + const videoUploadInput = document.getElementById('video-upload'); + const uploadedVideoVidCont = document.getElementById('uploaded-video-vid-cont'); + const uploadedVideoVid = document.getElementById('uploaded-video-vid'); + const uploadedVideoDiv = document.getElementById('uploaded-video-div'); + + videoUploadInput.addEventListener('change', function() { + const file = videoUploadInput.files[0]; + if (file) { + // Buat URL objek untuk video yang diunggah + const videoURL = URL.createObjectURL(file); + // uploadedVideo.src = videoURL; + uploadedVideoVidCont.src = videoURL; + uploadedVideoVid.style.display = 'block'; + uploadedVideoVidCont.style.display = 'block'; + uploadedVideoDiv.style.display = 'none'; + } + }); + + // Unggah Thumbnail + const thumbnailUploadInput = document.getElementById('thumbnail-upload'); + const uploadedThumbnailImg = document.getElementById('uploaded-thumbnail-img'); + const uploadedThumbnailDiv = document.getElementById('uploaded-thumbnail-div'); + + thumbnailUploadInput.addEventListener('change', function() { + const file = thumbnailUploadInput.files[0]; + if (file) { + // Buat URL objek untuk thumbnail yang diunggah + const thumbnailURL = URL.createObjectURL(file); + uploadedThumbnailImg.src = thumbnailURL; + uploadedThumbnailImg.style.display = 'block'; + uploadedThumbnailDiv.style.display = 'none'; + } + }); + + // // Menggantikan karakter Enter dengan elemen <br/> (seperti yang telah dijelaskan sebelumnya) + // const noteContent = document.querySelector('.note-content'); + // noteContent.innerHTML = noteContent.innerHTML.replace(/\n/g, '<br/>'); + </script> + + </div> + </body> +</html> \ No newline at end of file diff --git a/app/views/notes/content.php b/app/views/notes/content.php new file mode 100644 index 0000000000000000000000000000000000000000..a19edcfe3b3bdd3e6274822dafd18f5e0e41732b --- /dev/null +++ b/app/views/notes/content.php @@ -0,0 +1,83 @@ +<?php + include_once 'app/core/Database.php'; +?> + +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title> NotesApp </title> + <link rel="stylesheet" href="../../../public/css/notecontent.css" /> + <link rel="stylesheet" href="../../../public/css/styles.css" /> + <link + rel="stylesheet" + href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" + integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" + crossorigin="anonymous" + referrerpolicy="no-referrer" + /> + <!-- <script src="../../../public/js/utility.js"></script> --> + <script src="../../../public/js/notecontent.js"></script> + <script src="../../../public/js/removenote.js"></script> + <script src="../../../public/js/navbar.js"></script> + </head> + + <body> + <div class="navbar"> + <?php + include(__DIR__ . '/../navbar/navbar.php'); + ?> + </div> + <div class="main-container"> + <div class="top-buttons"> + <div class="back-button" onclick=backContent() onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"> + <button type="submit" class="search-button"> + <i class="fa-solid fa-chevron-left"></i> + </button> + <div class="back"> Back </div> + </div> + + <div class="util-buttons"> + <button type="submit" class="search-button" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick=editNote(<?php echo $data['id'] ?>)> + <i class="fa-solid fa-pen"></i> + </button> + <button type="submit" class="search-button" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick=confirmation(<?php echo $data['id'] ?>)> + <i class="fa-solid fa-trash-can"></i> + </button> + + </div> + </div> + <div class="note-top-container"> + <div class="note-informations-container"> + <div class="note-title"> + </div> + <hr> + <div class="tags"> + </div> + </div> + <div class="note-video"> + <video controls src="#" class="video-media"> + Maaf, browser Anda tidak mendukung elemen video. + </video> + </div> + </div> + + <div class="note-content"> + </div> + </div> + <div class="popup-alert" id="popup-alert"> + <div class="flex-column align-center"> + <i class="fas fa-triangle-exclamation" id="alertIcon"></i> + <p class="text">Apakah Anda yakin mau menghapus ini?</p> + <div class="flex-row align-center confirm-buttons"> + <button class="box align-center hapus" id="hapus" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">Hapus</button> + <button class="box align-center batal" id="batal" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">Batal</button> + </div> + </div> + </div> + <script> + getNoteContent(<?php echo $data['id'] ?>); + </script> + </body> +</html> \ No newline at end of file diff --git a/app/views/notes/editnote.php b/app/views/notes/editnote.php new file mode 100644 index 0000000000000000000000000000000000000000..d526867637db7eea6c610811adb18e923d0f80a9 --- /dev/null +++ b/app/views/notes/editnote.php @@ -0,0 +1,200 @@ +<?php + include_once 'app/core/Database.php'; +?> + +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title> NotesApp </title> + <link rel="stylesheet" href="../../../public/css/editnote.css" /> + <!-- <link rel="stylesheet" href="../../../public/css/styles.css" /> --> + <link + rel="stylesheet" + href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" + integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" + crossorigin="anonymous" + referrerpolicy="no-referrer" + /> + <script src="../../public/js/noteedit.js"></script> + <script src="../../public/js/removenote.js"></script> + <!-- <script src="../../../public/js/navbar.js"></script> --> + </head> + + <body> + <div class="navbar"> + <?php + include(__DIR__ . '/../navbar/navbar.php'); + ?> + </div> + <div class="main-container"> + <div class="top-buttons"> + <div class="back-button" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick=backEdit(<?php echo $data['id'] ?>)> + <button class="search-button" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick=backEdit(<?php echo $data['id'] ?>)> + <i class="fa-solid fa-chevron-left"></i> + </button> + <div class="back"> Back </div> + </div> + </div> + <div class="new-note-title"> + Edit note + </div> + <div class="note-data-cont"> + <div class="note-text-cont"> + <div class="text-input-cont"> + <input type="text" name="text-input" class="text-input" placeholder="Note title"> + </div> + + <div class="textarea-input-cont"> + <textarea name="textarea-input" class="textarea-input" placeholder="Note content"></textarea> + </div> + </div> + <div class="tags-media-cont"> + <div class="tags-section-cont"> + <div class="tags-button-cont"> + <button class="tags-button" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick=toggleChooseTag()> + <i class="fa-solid fa-tags"></i> + Tags + </button> + </div> + <div id="tags-cont"> + <div class="tag"> + music + </div> + <div class="tag"> + school + </div> + <div class="tag"> + sports + </div> + </div> + <div class="tags-list-container" style="display: none;"> + <?php + $tags = $data['allTagsData']; + foreach ($tags as $tag) { + if ($tag->tag_name != 'All') { + echo '<div class="tag" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick="chooseTag(\'' . $tag->tag_name . '\')">' . $tag->tag_name . '</div>'; + } + } + ?> + </div> + </div> + + <div class="media-section-cont"> + <div class="thumbnail-cont"> + <!-- Input Unggah Thumbnail --> + <div class="upload-thumbnail"> + <input type="file" id="thumbnail-upload" accept="image/*" hidden> + <label for="thumbnail-upload" class="upload-button"> + <div class="thumbnail-upload-button"> + <i class="fa-solid fa-upload"></i> Upload Thumbnail + </div> + </label> + </div> + + <!-- Tampilkan Thumbnail yang Diunggah --> + <div class="note-thumbnail"> + <img src="#" alt="Thumbnail" id="uploaded-thumbnail-img"> + <div id="uploaded-thumbnail-div"> + Note Thumbnail + </div> + </div> + </div> + + <div class="video-cont"> + <!-- Input Unggah Video --> + <div class="upload-video"> + <input type="file" id="video-upload" accept="video/*" hidden> + <label for="video-upload"> + <div class="video-upload-button"> + <i class="fa-solid fa-upload"></i> Upload Video + </div> + </label> + </div> + + <!-- Tampilkan Video yang Diunggah --> + <div class="note-video"> + <video controls width="100%" height="auto" id="uploaded-video-vid-cont"> + <source src="../../../storage/note/vid/Visualisasi mimpi.mp4" id="uploaded-video-vid" type="video/mp4"> + </video> + + <div id="uploaded-video-div"> + Note Video + </div> + </div> + </div> + </div> + + + <!-- <div class="add-note" onclick="saveEditedNote(<?php echo $data['user_id']; ?>" > --> + <div class="add-note" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick="saveEditedNote(<?php echo $data['id'] ?>, <?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>)"> + <i class="fa-solid fa-plus"></i> + Save Note + </div> + <div class="delete-note" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick="confirmation(<?php echo $data['id'] ?>)"> + <i class="fa-solid fa-trash"></i> + Delete Note + </div> + </div> + + </div> + <div> + <div class="popup-alert" id="popup-alert"> + <div class="flex-column align-center"> + <i class="fas fa-triangle-exclamation" id="alertIcon"></i> + <p class="text">Apakah Anda yakin mau menghapus ini?</p> + <div class="flex-row align-center confirm-buttons"> + <button class="box align-center hapus" id="hapus" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">Hapus</button> + <button class="box align-center batal" id="batal" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">Batal</button> + </div> + </div> + </div> + <script> + // Unggah Video + const videoUploadInput = document.getElementById('video-upload'); + const uploadedVideoVidCont = document.getElementById('uploaded-video-vid-cont'); + const uploadedVideoVid = document.getElementById('uploaded-video-vid'); + const uploadedVideoDiv = document.getElementById('uploaded-video-div'); + + videoUploadInput.addEventListener('change', function() { + const file = videoUploadInput.files[0]; + if (file) { + // Buat URL objek untuk video yang diunggah + const videoURL = URL.createObjectURL(file); + // uploadedVideo.src = videoURL; + uploadedVideoVidCont.src = videoURL; + uploadedVideoVid.style.display = 'block'; + uploadedVideoVidCont.style.display = 'block'; + uploadedVideoDiv.style.display = 'none'; + } + }); + + // Unggah Thumbnail + const thumbnailUploadInput = document.getElementById('thumbnail-upload'); + const uploadedThumbnailImg = document.getElementById('uploaded-thumbnail-img'); + const uploadedThumbnailDiv = document.getElementById('uploaded-thumbnail-div'); + + thumbnailUploadInput.addEventListener('change', function() { + const file = thumbnailUploadInput.files[0]; + if (file) { + // Buat URL objek untuk thumbnail yang diunggah + const thumbnailURL = URL.createObjectURL(file); + uploadedThumbnailImg.src = thumbnailURL; + uploadedThumbnailImg.style.display = 'block'; + uploadedThumbnailDiv.style.display = 'none'; + } + }); + + // // Menggantikan karakter Enter dengan elemen <br/> (seperti yang telah dijelaskan sebelumnya) + // const noteContent = document.querySelector('.note-content'); + // noteContent.innerHTML = noteContent.innerHTML.replace(/\n/g, '<br/>'); + </script> + + </div> + + <script> + getNoteContent(<?php echo $data['id'] ?>); + </script> + </body> +</html> \ No newline at end of file diff --git a/app/views/notes/index.php b/app/views/notes/index.php new file mode 100644 index 0000000000000000000000000000000000000000..6045c3aca54638f031c466212640a26430fd715d --- /dev/null +++ b/app/views/notes/index.php @@ -0,0 +1,103 @@ +<?php + include_once 'app/core/Database.php'; +?> + +<!DOCTYPE html> +<html lang="en"> + + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title> NotesApp </title> + <link rel="stylesheet" href="../../../public/css/notes.css" /> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossorigin="anonymous" referrerpolicy="no-referrer" /> + <script src="../../../public/js/searchnote.js"></script> + <!-- <script src="../../../public/js/navbar.js"></script> --> + </head> + + <body> + <div class="navbar"> + <?php + include(__DIR__ . '/../navbar/navbar.php'); + ?> + </div> + + <div class="main-container"> + <div class="searchbar-container"> + <div class="search-box"> + <button type="submit" class="search-button" onclick="searchNote('', <?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>)"><i class="fa fa-search"></i></button> + <input type="text" class="searchTerm" id="search-title-input"> + <!-- <button class="tags-button" onclick=openChooseTags()> Tags <i class="fa fa-tags"></i> </button> --> + <button id="sort-button" onclick=toggleSort()> + Date + <i class="fa fa-sort-amount-down"></i> + </button> + <button class="tags-button" onclick=toggleChooseTag()> Tags <i class="fa fa-tags"></i> </button> + </input> + </div class="note-button-cont"> + <button type="submit" class="note-button" onclick="addNote(<?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>)"> + Note </button> + <!-- <button type="submit" class="note-button" onclick="addNote(<?php echo $data['user_id'] ?>, 1)"> + Note </button> --> + </div> + <div class="tags-list-container" style="display: none;"> + <?php + $tags = $data['allTagsData']; + foreach ($tags as $tag) { + echo '<div class="tag" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick="chooseTag(\'' . $tag->tag_name . '\')">' . $tag->tag_name . '</div>'; + } + ?> + </div> + </div> + + <div class="notes-container"> + <div id="notes-cards-container"> + </div> + <div class="pagination-cont"> + <div class="page-text"> + Page: + </div> + <div class="notes-pagination"> + <!-- <select id="page-dropdown" onchange="changePage(<?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>)"> --> + <select id="page-dropdown" onchange="changePage(<?php echo $data['user_id'] ?>, 1)"> + </select> + </div> + </div> + </div> + <script> + // JANGAN LUPA DIGANTI!!!!! + // JANGAN LUPA DIGANTI!!!!! + // JANGAN LUPA DIGANTI!!!!! + // JANGAN LUPA DIGANTI!!!!! + searchNote("<?php echo $data['path'] ?>", <?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>); + listPages(<?php echo $data['user_id']; ?>, <?php echo $_SESSION['is_admin'] ?>); + // listPages(<?php echo $data['user_id']; ?>, 1); + </script> + + <script> + toggleSort(); + setPageNum(); + setTitleInput(); + let titleInput = document.getElementById("search-title-input"); // Ambil elemen input judul + let tagElements = document.getElementsByClassName("tag"); // Ambil elemen-elemen tag + let sortButton = document.getElementById("sort-button"); // Ambil elemen tombol sort + + titleInput.addEventListener("input", function () { + debounceSearchNote(<?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>); + // debounceSearchNote(<?php echo $data['user_id'] ?>, 1); + }); + for (let i = 0; i < tagElements.length; i++) { + tagElements[i].addEventListener("click", function () { + debounceSearchNote(<?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>); + // debounceSearchNote(<?php echo $data['user_id'] ?>, 1); + + }); // Tambahkan event listener pada elemen-elemen tag + } + sortButton.addEventListener("click", function () { + debounceSearchNote(<?php echo $data['user_id'] ?>, <?php echo $_SESSION['is_admin'] ?>); + // debounceSearchNote(<?php echo $data['user_id'] ?>, 1); + + }); + </script> + </div> + </body> +</html> \ No newline at end of file diff --git a/app/views/profilepage/index.php b/app/views/profilepage/index.php new file mode 100644 index 0000000000000000000000000000000000000000..db8bf916d7e1aa57c294ad8e6b4016ec68d35b86 --- /dev/null +++ b/app/views/profilepage/index.php @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>signup</title> + <link rel="stylesheet" href="../../../public/css/profilpage.css" /> + <script src="../../../public/js/useredit.js"></script> +</head> +<body> + <div class="container"> + <div class="page-data"> + <!-- <form onsubmit="saveUserProfile(<?php echo $data['user_id'] ?>)"> --> + <form> + <div class="notes"> + <h2>Update Profile</h2> + <ul class="notes-reminder-card"> + <li class="card"> + <h4 class="header-box">New Email</h4> + <div class="input-field"> + <input type="email" id="email-input" placeholder="Enter your new Email" required> + </div> + </li> + <li class="card"> + <h4 class="header-box">New Password</h4> + <div class="input-field"> + <input type="password" id="password-input" placeholder="Enter your new Password" required> + </div> + </li> + </ul> + </div> + <div class="wrap-btn"> + <button type="submit" onclick="saveUserProfile(<?php echo $data['user_id'] ?>)" class="submit-btn">Submit</button> + </div> + </form> + </div> + </div> +</body> +</html> diff --git a/app/views/reminder/index.php b/app/views/reminder/index.php new file mode 100644 index 0000000000000000000000000000000000000000..3349861c6f34daf516de7e9f081f488c54cf05f9 --- /dev/null +++ b/app/views/reminder/index.php @@ -0,0 +1,153 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Reminder</title> + <link rel="stylesheet" href="../../../public/css/reminder.css" /> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"> + </head> + <body> + + <div class="navbar"> + <?php + include(__DIR__ . '/../navbar/navbar.php'); + ?> + </div> + + <div class="reminder flex-column align-center"> + + <div class="search flex-row align-center"> + <div class="search-1 flex-row box align-center"> + <div class="searchbar"> + <input type="text" placeholder="Search..." id="searchInput" /> + </div> + <?php + $user_id = $data['user_id']; + + echo '<div class="time-filter flex-row box align-center" onclick="toggleSort(\'' . $user_id . '\')" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">' + ?> + <div class="text">Time</div> + <i class="fas fa-angle-up" id="sortIcon"></i> + + </div> + <div class="category-filter flex-row box align-center" onclick="toggleCategoryList()" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"> + <div class="text" id="category-title">Category</div> + <i class="fas fa-tags"></i> + </div> + <div class="category-list flex-column"> + <?php + $categories = $data['allTagsData']; + foreach ($categories as $category) { + echo '<div class="category" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick="filterByCategory(\'' . $category->tag_name . '\')">' . $category->tag_name . '</div>'; + } + ?> + </div> + + </div> + <div class="add-button flex-row box align-center" onclick="addReminder()" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"> + <i class="fas fa-plus"></i> + <div class="text">Reminders</div> + </div> + </div> + + <div class="reminders-content"> + <?php + + $itemCount = 0; + + echo '<div class="reminder-row">'; + $reminders = $data['allRemindersData']; + foreach ($reminders as $reminder) { + $deadlineDateDisplay = date('d M Y H:i', strtotime($reminder->deadline)); + echo '<div class="reminder-item box " onclick="getReminderContent(\'' . $reminder->reminder_id . '\')" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">'; + echo '<div class="content-title text">' . $reminder->title . '</div>'; + $contentText = $reminder->tasks; + if (strlen($contentText) > 110) { + $contentText = substr($contentText, 0, 110) . '...'; + } + echo '<p class="content-text text">' . $contentText . '</p>'; + echo '<div class="deadline-category-box flex-row align-bottom align-center">'; + echo '<div class="content-deadline text box">' . $deadlineDateDisplay . '</div>'; + echo '<div class="content-category">' . $reminder->category->category_name . '</div>'.'</div>'; + echo '</div>'; + + $itemCount++; + + if ($itemCount % 3 === 0) { + echo '</div>'; + if ($itemCount < count($reminders)) { + echo '<div class="reminder-row">'; + } + } + } + + if ($itemCount % 3 !== 0) { + echo '</div>'; + } + ?> + </div> + + <div class="popup-cover" id="popup-cover"></div> + + <div class="popup flex-column" id="popup"> + <div class="popup-header flex-row"> + <i class="fas fa-id-card fa-2x"></i> + <div class = "h2 semi-bold popup-title-input box" id="popup-title-input" contentEditable="false"></div> + <div class = "h2 semi-bold popup-title" id="popup-title">Title</div> + <div class="edit-icon flex-row"> + <i class="fas fa-pen fa-2x" id="edit-icon" onclick="editReminder()" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"></i> + <i class="fas fa-trash fa-2x" id="trash-icon" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"></i> + <img class="icon-popup" id="close-icon" src="../../../public/images/xmark.svg" onclick="closePopup()" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" /> + </div> + </div> + <div class="popup-category-deadline flex-row"> + <div class="popup-category flex-column"> + <div class="popup-category-1 relative">Category</div> + <div class="content-category-input align-center" id="popup-category-2-input" onclick="toggleCategoryListInput()" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)"></div> + <div class="content-category relative" id="popup-category-2">Category</div> + </div> + <div class="popup-deadline flex-column"> + <div class="popup-deadline-1 relative">Deadline</div> + <input type="datetime-local" class="content-deadline-input text box" id="popup-deadline-2-input"> + <div class="content-deadline text box" id="popup-deadline-2">Deadline</div> + </div> + </div> + <div class="popup-body-header flex-row align-center"> + <i class="fas fa-newspaper fa-2x"></i> + <div class = "h2 semi-bold popup-task">Task</div> + </div> + <div class="popup-body"> + <div class="popup-text-input box overflow-y" id="popup-text-input"></div> + <div class="popup-text overflow-y" id="popup-text">task</div> + </div> + <?php + $user_id = $data['user_id']; + echo '<div class="add box" id="add" onclick="add(\'' . $user_id . '\')" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">Submit</div>'; + ?> + <div class="submit box" id="submit" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">Submit</div> + </div> + + <div class="category-list-input flex-column"> + <?php + foreach ($categories as $category) { + echo '<div class="category" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)" onclick="filterByCategoryInput(\'' . $category->tag_name . '\')">' . $category->tag_name . '</div>'; + } + ?> + </div> + + <div class="popup-alert" id="popup-alert"> + <div class="flex-column align-center"> + <i class="fas fa-triangle-exclamation" id="alertIcon"></i> + <p class="text">Apakah Anda yakin mau menghapus ini?</p> + <div class="flex-row align-center confirm-buttons"> + <button class="box align-center hapus" id="hapus" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">Hapus</button> + <button class="box align-center batal" id="batal" onmouseover="setCursorPointer(this)" onmouseout="resetCursor(this)">Batal</button> + </div> + </div> + </div> + </div> + + <script src="../../../public/js/reminder.js"></script> + </body> +</html> diff --git a/config/config.php b/config/config.php new file mode 100644 index 0000000000000000000000000000000000000000..3712892cc8b6ffad58012f2ed7ebf9b77d0654d6 --- /dev/null +++ b/config/config.php @@ -0,0 +1,11 @@ +<?php + +define('BASE_URL', 'http://localhost:8008/public'); + +define('DB_HOST', getenv('POSTGRES_HOST')); +define('DB_USER', getenv('POSTGRES_USER')); +define('DB_PASSWORD', getenv('POSTGRES_PASSWORD')); +define('DB_PORT', getenv('POSTGRES_PORT')); +define('DB_NAME', getenv('POSTGRES_DB')); + +define('DEBOUNCE_TIMEOUT', 500); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..4e799aa43297746fe537fe802c4d64db54e1d64a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.3' +services: + web: + image: tubes-1:latest + volumes: + - .:/var/www/html + ports: + - 8008:80 + build: + context: . + dockerfile: Dockerfile + env_file: .env + + db: + image: postgres:latest + hostname: db-tubes-1 + ports: + - 5432:5432 + volumes: + - ./postgres:/var/lib/postgresql + env_file: .env + restart: always diff --git a/env_example.txt b/env_example.txt new file mode 100644 index 0000000000000000000000000000000000000000..bead8c5bb89d4e774dae6cd64c229d47a71419b6 --- /dev/null +++ b/env_example.txt @@ -0,0 +1,5 @@ +POSTGRES_HOST=db-tubes-1 +POSTGRES_DB=tubeswbd1 +POSTGRES_USER= +POSTGRES_PASSWORD= +POSTGRES_PORT=5432 \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000000000000000000000000000000000000..d35f6d66827755c926d5c1ace41aa81b5077d286 --- /dev/null +++ b/index.php @@ -0,0 +1,18 @@ +<?php +if(!session_id()) session_start(); + +require_once 'app/init.php'; +$app = new App; + +?> + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>MyWeb</title> +</head> +<body> +</body> +</html> \ No newline at end of file diff --git a/public/css/addnote.css b/public/css/addnote.css new file mode 100644 index 0000000000000000000000000000000000000000..f94b89cb6c3d1ae23124eec99028d38e05991685 --- /dev/null +++ b/public/css/addnote.css @@ -0,0 +1,274 @@ +* { + font-family: 'Montserrat', sans-serif; + color: #323643; + background-color: #F7F3F4; + box-sizing: border-box; +} +.navbar{ + position: sticky; + top: 0; + z-index: 100; +} +.main-container { + display: flex; + flex-direction: column; + align-items: center; + padding: 70px 3% 0 3%; + margin: 0 auto 50px auto; +} + +button { + border-width: 0px; +} + +.top-buttons { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + margin-bottom: 20px; +} + +.back-button { + display: flex; + flex-direction: row; + align-items: center; + font-size: 30px; + font-style: normal; + font-weight: 600; + line-height: normal; +} + +.back { + color: #606470; +} + + +.fa-chevron-left { + font-size: 30px; + font-style: normal; + font-weight: 600; + line-height: normal; + margin-right: 10px; +} + +.fa-chevron-left { + color: #606470; +} + +.fa-tags, .fa-plus { + color: #F7F3F4; + background-color: transparent; + margin-right: 10px; +} + +.new-note-title { + display: flex; + width: 95%; + justify-content: start; + font-size: 40px; + color: #606470; + font-weight: 700; + margin-bottom: 30px; + text-align: left; +} + +.note-data-cont { + display: flex; + width: 95%; + flex-direction: row; + flex-wrap: wrap; +} + +.note-text-cont { + display: flex; + flex-direction: column; + flex-grow: 1; +} + +.text-input-cont { + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + border: 2px solid #323643; + border-radius: 20px; + margin-bottom: 40px; + font-size: 30px; + width: 95%; +} + +.text-input { + border-radius: 20px; + border-width: 0px; + padding: 10px 20px; + font-size: 30px; + width: 100%; +} + +.text-input:focus, .textarea-input:focus { + outline: none; +} + +.text-input::placeholder, .textarea-input::placeholder { + color: #d9d9d9; +} + +.textarea-input-cont { + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + border: 2px solid #323643; + border-radius: 20px; + margin: 5px 0; + width: 95%; + padding: 10px 5px; +} + +.textarea-input { + resize: none; + width: 100%; + height: 105vh; + border-width: 0; + font-size: 20px; + font-weight: 400; + padding: 10px 20px; +} + +.tags-media-cont { + width: 350px; +} + +.tags-section-cont { + width: 100%; +} + +.tags-button-cont { + width: 100%; +} + +.tags-button { + border: 3px solid rgba(50, 54, 67, 0.4); + border-radius: 10px; + display: flex; + background-color: #323643; + color: #F7F3F4; + align-items: center; + font-size: 20px; + padding: 10px; + font-weight: 600; + margin-bottom: 10px; + height: 50px; +} + +#tags-cont { + display: flex; + justify-content: space-even; + width: 76%; + border: 2px solid rgba(50, 54, 67, 0.4); + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + border-radius: 10px; + padding: 10px; + margin-bottom: 30px; + flex-wrap: wrap; +} + +.tag { + width: 100px; + font-size: 18px; + font-weight: 500; + margin: 5px 10px 5px 10px; + background-color: #323643; + color: #F7F3F4; + border-radius: 10px; + text-align: center; +} + +.tags-list-container { + /* display: none; */ + flex-direction: column; + height: auto; + width: fit-content; + max-height: 200px; + overflow-y: auto; + border: 1px solid #323643; + border-radius: 10px; + margin-bottom: 30px; +} + +.note-thumbnail, .note-video { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 30px; + height: 180px; + width: 100%; +} + +#uploaded-thumbnail-div, #uploaded-video-div { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + border: 2px dashed #606470; +} + +#uploaded-thumbnail-img { + display: none; + max-height: 100%; + max-width: 100%; + width: auto; +} + +.thumbnail-upload-button, .video-upload-button { + display: flex; + justify-content: space-evenly; + align-items: center; + width: 100%; + padding: 2% 10%; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + border-radius: 10px; + font-size: 20px; + margin-bottom: 10px; + border: 2px solid rgba(50, 54, 67, 0.4); +} + +#uploaded-video-vid-cont, #uploaded-video-vid { + display: none; + max-height: 100%; + max-width: 100%; + width: auto; +} + +.add-note { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 10px; + border-radius: 10px; + font-size: 25px; + background-color: #323643; + color: #F7F3F4; +} + +@media screen and (max-width: 900px) { + .tags-media-cont { + width: 100%; + } + + .tags-button-cont { + margin-top: 30px; + } + + .textarea-input-cont, .text-input-cont { + width: 100%; + } + + .media-section-cont { + display: flex; + width: 100%; + flex-direction: row; + justify-content: space-between; + } + + .thumbnail-cont, .video-cont { + width: 48%; + } +} \ No newline at end of file diff --git a/public/css/admin.css b/public/css/admin.css new file mode 100644 index 0000000000000000000000000000000000000000..59a27d6576666465347766ce4cc44fcc02b9f48e --- /dev/null +++ b/public/css/admin.css @@ -0,0 +1,114 @@ +* { + font-family: 'Montserrat', sans-serif; + color: #323643; + background-color: #F7F3F4; + box-sizing: border-box; +} + +.navbar{ + position: sticky; + top: 0; + z-index: 100; +} +.main-container { + display: flex; + flex-direction: column; + align-items: center; + padding: 5% 10% 0 10%; + margin: 30px auto; +} + +.top-container { + font-size: 30px; + font-weight: 700; + margin-bottom: 30px; +} + +.user-table-container { + width: 100%; +} + +.table-header-row-container { + display: flex; + flex-direction: row; + width: 100%; + height: fit-content; + border: 1px solid #323643; + font-size: 20px; + font-weight: 600; +} + +.user-id, .id-col-container { + width: 100px; + flex-shrink: 0; + display: flex; + justify-content: center; + height: auto; + align-items: center; + border: 1px solid #323643; + text-align: center; +} + +.email, .username, .email-col-container, .username-col-container { + display: flex; + /* flex-shrink: 0; */ + align-items: center; + justify-content: center; + height: auto; + flex-grow: 1; + width: 200px; + border: 1px solid #323643; + text-align: center; +} + +.email-col-container, .username-col-container { + padding: 0 10px; + flex-shrink: 0; + + justify-content: start; +} + +.notes, .notes-col-container { + width: 100px; + flex-shrink: 0; + display: flex; + justify-content: center; + height: auto; + align-items: center; + text-align: center; + border: 1px solid #323643; +} + +.reminder, .reminder-col-container { + width: 140px; + flex-shrink: 0; + display: flex; + justify-content: center; + height: auto; + align-items: center; + text-align: center; + border: 1px solid #323643; +} + +.delete, .delete-col-container, .edit, .edit-col-container { + width: 100px; + flex-shrink: 0; + display: flex; + justify-content: center; + height: auto; + align-items: center; + text-align: center; + border: 1px solid #323643; +} + +.row-container { + display: flex; + flex-shrink: 0; + flex-direction: row; + width: 100%; + height: fit-content; + border: 1px solid #323643; + font-size: 16px; + font-weight: 500; +} + diff --git a/public/css/editnote.css b/public/css/editnote.css new file mode 100644 index 0000000000000000000000000000000000000000..76c3c3b08968f29a2f91a44dee594fb068875ef3 --- /dev/null +++ b/public/css/editnote.css @@ -0,0 +1,340 @@ +* { + font-family: 'Montserrat', sans-serif; + color: #323643; + background-color: #F7F3F4; + box-sizing: border-box; +} +.navbar{ + position: sticky; + top: 0; + z-index: 100; +} +.main-container { + display: flex; + flex-direction: column; + align-items: center; + padding: 70px 3% 0 3%; + margin: 0 auto 50px auto; +} + +button { + border-width: 0px; +} + +.top-buttons { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + margin-bottom: 20px; +} + +.back-button { + display: flex; + flex-direction: row; + align-items: center; + font-size: 30px; + font-style: normal; + font-weight: 600; + line-height: normal; +} + +.back { + color: #606470; +} + + +.fa-chevron-left { + font-size: 30px; + font-style: normal; + font-weight: 600; + line-height: normal; + margin-right: 10px; +} + +.fa-chevron-left { + color: #606470; +} + +.fa-tags, .fa-plus, .fa-trash { + color: #F7F3F4; + background-color: transparent; + margin-right: 10px; +} + +.new-note-title { + display: flex; + width: 95%; + justify-content: start; + font-size: 40px; + color: #606470; + font-weight: 700; + margin-bottom: 30px; + text-align: left; +} + +.note-data-cont { + display: flex; + width: 95%; + flex-direction: row; + flex-wrap: wrap; +} + +.note-text-cont { + display: flex; + flex-direction: column; + flex-grow: 1; +} + +.text-input-cont { + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + border: 2px solid #323643; + border-radius: 20px; + margin-bottom: 40px; + font-size: 30px; + width: 95%; +} + +.text-input { + border-radius: 20px; + border-width: 0px; + padding: 10px 20px; + font-size: 30px; + width: 100%; +} + +.text-input:focus, .textarea-input:focus { + outline: none; +} + +.text-input::placeholder, .textarea-input::placeholder { + color: #d9d9d9; +} + +.textarea-input-cont { + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + border: 2px solid #323643; + border-radius: 20px; + margin: 5px 0; + width: 95%; + padding: 10px 5px; +} + +.textarea-input { + resize: none; + width: 100%; + height: 105vh; + border-width: 0; + font-size: 20px; + font-weight: 400; + padding: 10px 20px; +} + +.tags-media-cont { + width: 350px; +} + +.tags-section-cont { + width: 100%; +} + +.tags-button-cont { + width: 100%; +} + +.tags-button { + border: 3px solid rgba(50, 54, 67, 0.4); + border-radius: 10px; + display: flex; + background-color: #323643; + color: #F7F3F4; + align-items: center; + font-size: 20px; + padding: 10px; + font-weight: 600; + margin-bottom: 10px; + height: 50px; +} + +#tags-cont { + display: flex; + justify-content: space-even; + width: 76%; + border: 2px solid rgba(50, 54, 67, 0.4); + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + border-radius: 10px; + padding: 10px; + margin-bottom: 30px; + flex-wrap: wrap; +} + +.tag { + width: 100px; + font-size: 18px; + font-weight: 500; + margin: 5px 10px 5px 10px; + background-color: #323643; + color: #F7F3F4; + border-radius: 10px; + text-align: center; +} + +.tags-list-container { + /* display: none; */ + flex-direction: column; + height: auto; + width: fit-content; + max-height: 200px; + overflow-y: auto; + border: 1px solid #323643; + border-radius: 10px; + margin-bottom: 30px; +} + +.note-thumbnail, .note-video { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 30px; + height: 180px; + width: 100%; +} + +#uploaded-thumbnail-div, #uploaded-video-div { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + border: 2px dashed #606470; +} + +#uploaded-thumbnail-img { + display: none; + max-height: 100%; + max-width: 100%; + width: auto; +} + +.thumbnail-upload-button, .video-upload-button { + display: flex; + justify-content: space-evenly; + align-items: center; + width: 100%; + padding: 2% 10%; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + border-radius: 10px; + font-size: 20px; + margin-bottom: 10px; + border: 2px solid rgba(50, 54, 67, 0.4); +} + +#uploaded-video-vid-cont, #uploaded-video-vid { + display: none; + max-height: 100%; + max-width: 100%; + width: auto; +} + +.add-note, .delete-note { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 10px; + border-radius: 10px; + font-size: 25px; + color: #F7F3F4; + margin: 15px 0; +} + +.add-note { + background-color: #323643; +} + +.delete-note { + background-color: #C70000; +} +.popup-alert{ + display: none; + position: fixed; + width: 350px; + height: 280px; + top: 50%; + left: 50%; + padding: 20px 20px; + gap: 20px; + transform: translate(-50%, -50%); + align-items: start; + justify-content:unset; + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; + z-index: 4; +} +.fa-triangle-exclamation{ + font-size: 90px; + color: rgb(224, 220, 62) +} +.confirm-buttons{ + gap: 50px; +} +.hapus{ + font-size: 20px; + color: white; + background-color: red !important; + padding: 15px; +} +.batal{ + font-size: 20px; + padding: 15px; +} +.text{ + font-family: Montserrat; +} +.flex-row{ + position: relative; + display: flex; +} +.flex-column{ + position: relative; + display: flex; + flex-direction: column; +} +.box{ + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; +} +.align-center{ + justify-content: center; + align-items: center; +} +@media screen and (max-width: 900px) { + .tags-media-cont { + width: 100%; + } + + .tags-button-cont { + margin-top: 30px; + } + + .textarea-input-cont, .text-input-cont { + width: 100%; + } + + .media-section-cont { + display: flex; + width: 100%; + flex-direction: row; + justify-content: space-between; + } + + .thumbnail-cont, .video-cont { + width: 48%; + } +} \ No newline at end of file diff --git a/public/css/error.css b/public/css/error.css new file mode 100644 index 0000000000000000000000000000000000000000..3f7af21d7ee29dc7a156c74db0a241b7fd64c376 --- /dev/null +++ b/public/css/error.css @@ -0,0 +1,39 @@ +.html, body{ + background-color: #F7F3F4; +} +.text{ + font-family: Montserrat; +} +.flex-column{ + position: relative; + display: flex; + flex-direction: column; + height: 100vh; + margin: auto; +} +.align-center{ + justify-content: center; + align-items: center; +} +h1 { + font-size: 80px; + margin-bottom: 0px; +} +p { + font-size: 40px; +} + +button { + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; +} + +a { + text-decoration: none; + font-size: 40px; + color: black; + padding: 20px; +} \ No newline at end of file diff --git a/public/css/home.css b/public/css/home.css new file mode 100644 index 0000000000000000000000000000000000000000..90a7c919857aa202bc4a8d3f0d91e62255639ab3 --- /dev/null +++ b/public/css/home.css @@ -0,0 +1,55 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat&display=swap'); + +.html, body{ + background-color: #F7F3F4; +} +.text{ + font-family: Montserrat; + font-size: 35px; + font-weight:700; +} +.flex-row{ + position: relative; + display: flex; + gap: 100px; +} +.flex-column{ + position: relative; + display: flex; + flex-direction: column; + gap: 20px; +} +.box{ + width: 400px; + height: 400px; + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; +} +.align-center{ + justify-content: center; + align-items: center; +} +.center{ + align-items: center; +} +.navbar{ + position: sticky; + top: 0; + z-index: 2; +} +.number{ + font-family: Montserrat; + font-size: 90px; + font-weight: bold; +} +.judul_notes, .judul_reminders{ + margin-top: 50px; + margin-bottom: 50px; +} +.welcome{ + margin-left: 320px; + padding: 30px 0px; +} \ No newline at end of file diff --git a/public/css/navbar.css b/public/css/navbar.css new file mode 100644 index 0000000000000000000000000000000000000000..66ea9ec69b64934aa983e052fc34c4a52735e410 --- /dev/null +++ b/public/css/navbar.css @@ -0,0 +1,147 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat&display=swap'); + +.html, body { + margin: 0; + padding: 0; +} +.navbar-text{ + font-family: "Inter-SemiBold", Helvetica; + font-weight: 600; + color: #ffffff; + font-size: 18px; + letter-spacing: 0; + line-height: normal; + transition: color 0.5s ease; /* Efek transisi */ +} +.navbar .navbar-text:hover { + color: #93DEFF; +} +.navbar { + width: 100%; + height: 80px; + position: sticky; + display: flex; + top: 0; + justify-content: space-between; + align-items: center; + padding: 0 20px; + box-sizing: border-box; + background-color: #323643; +} +.navbar .navbar-div{ + position: relative; + display: flex; + gap: 75px; + align-items: center; + background-color: #323643; +} +.navbar .navbar-div a{ + text-decoration: none; +} +.navbar .reminders { + background-color: #323643; +} +.navbar .logo { + font-family: "Inter-SemiBold", Helvetica; + font-weight: 600; + color: #ffffff; + font-size: 24px; + letter-spacing: 0; + line-height: normal; + background-color: #323643; +} +.navbar .profile { + width: 50px; + height: 50px; + object-fit: cover; + background-color: #323643; +} +.navbar .profile:hover { + background-color: #93DEFF; +} +.navbar .notes { + margin-right: -20px; + background-color: #323643; +} +.navbar .home{ + background-color: #323643; +} +.navbar .admin{ + background-color: #323643; +} +.dropdown-content { + display: none; + position: absolute; + left: 70%; + background-color: #f9f9f9; + min-width: 160px; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 1; +} + +.dropdown-content a { + padding: 12px 16px; + text-decoration: none; + display: block; +} + +.dropdown:hover .dropdown-content { + display: block; +} + +.dropdown-content-1, .dropdown-content-2{ + color: #323643; + border: 1px solid; + border-color: #32364366; +} + +.dropdown-content-1:hover, .dropdown-content-2:hover{ + background-color: #93DEFF; +} +.dropdown-content-0, .profile-respon, +.dropdown-responsif{ + display: none; +} +@media screen and (max-width: 600px) { + .admin, .home, .reminders, .notes,.profile{ + display: none; + } + .dropdown-content-0,.more, + .dropdown-responsif{ + display: flex; + } + .navbar .more { + object-fit: cover; + font-size: 24px; + background-color: #323643; + color: white; + } + .dropdown-responsif:hover .dropdown-content { + display: block; + } + .dropdown-content { + display: none; + position: absolute; + left: -30px; + background-color: #f9f9f9; + min-width: 160px; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 1; + } + + .dropdown-content a { + padding: 12px 16px; + text-decoration: none; + display: block; + } + + .dropdown-content-0{ + color: #323643; + border: 1px solid; + border-color: #32364366; + } + .dropdown-content-0:hover{ + background-color: #93DEFF; + } + +} \ No newline at end of file diff --git a/public/css/notecontent.css b/public/css/notecontent.css new file mode 100644 index 0000000000000000000000000000000000000000..0c3eadc277ac4d11d41a57979b3ab7d7afeb6711 --- /dev/null +++ b/public/css/notecontent.css @@ -0,0 +1,232 @@ +* { + font-family: 'Montserrat', sans-serif; + color: #323643; + background-color: #F7F3F4; + box-sizing: border-box; +} +.navbar{ + position: sticky; + top: 0; + z-index: 100; +} +.main-container { + display: flex; + flex-direction: column; + align-items: center; + padding: 70px 3% 0 3%; + margin: 0 auto; +} + +button { + border-width: 0px; +} + +.top-buttons { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + margin-bottom: 20px; +} + +.back-button { + display: flex; + flex-direction: row; + align-items: center; + font-size: 30px; + font-style: normal; + font-weight: 600; + line-height: normal; +} + +.back { + color: #606470; +} + +.fa-chevron-left, .fa-pen, .fa-trash-can { + font-size: 30px; + font-style: normal; + font-weight: 600; + line-height: normal; + margin-right: 10px; +} + +.fa-chevron-left { + color: #606470; +} + +.util-buttons { + margin-right: 5%; +} + +.fa-pen, .fa-trash-can { + color: #323643; +} + +.note-top-container { + display: flex; + padding: 0 5%; + margin-bottom: 20px; + width: 100%; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; +} + +.note-informations-container { + display: flex; + width: 40%; + flex-direction: column; + margin: 10px; +} + +.note-title { + font-size: 60px; + font-weight: 600; +} + +.note-video { + width: 45%; + height: 300px; +} + +.video-media { + max-width: 100%; + height: auto; + max-height: 100%; +} + +.tags { + display: flex; + width: 100%; + flex-wrap: wrap; +} + +.tag { + width: 100px; + font-size: 18px; + font-weight: 500; + margin: 20px 10px 20px 10px; + background-color: #323643; + color: #F7F3F4; + border-radius: 10px; + text-align: center; +} + +.note-content { + width: 90%; + text-align: justify; + font-size: 16px; + font-weight: 300; + margin-bottom: 30px; +} +.popup-alert{ + display: none; + position: fixed; + width: 350px; + height: 280px; + top: 50%; + left: 50%; + padding: 20px 20px; + gap: 20px; + transform: translate(-50%, -50%); + align-items: start; + justify-content:unset; + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; + z-index: 4; +} +.fa-triangle-exclamation{ + font-size: 90px; + color: rgb(224, 220, 62) +} +.confirm-buttons{ + gap: 50px; +} +.hapus{ + font-size: 20px; + color: white; + background-color: red !important; + padding: 15px; +} +.batal{ + font-size: 20px; + padding: 15px; +} +.text{ + font-family: Montserrat; +} +.flex-row{ + position: relative; + display: flex; +} +.flex-column{ + position: relative; + display: flex; + flex-direction: column; +} +.box{ + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; +} +.align-center{ + justify-content: center; + align-items: center; +} + +@media screen and (max-width: 600px) { + .top-buttons { + width: 95%; + } + + .back-button { + font-size: 20px; + } + + .fa-chevron-left, .fa-pen, .fa-trash-can { + font-size: 20px; + } + + .note-top-container { + margin-bottom: 20px; + flex-direction: column; + } + + .note-informations-container { + width: 90%; + } + + .note-title { + font-size: 40px; + } + + .note-video { + width: 90%; + } + + .tag { + width: 80px; + font-size: 16px; + font-weight: 500; + margin: 10px 5px 10px 5px; + background-color: #323643; + color: #F7F3F4; + border-radius: 10px; + text-align: center; + } + + .note-content { + width: 90%; + text-align: justify; + font-size: 14px; + font-weight: 300; + margin-bottom: 30px; + } +} \ No newline at end of file diff --git a/public/css/notes.css b/public/css/notes.css new file mode 100644 index 0000000000000000000000000000000000000000..40028161b27b7cbc33e2af4e90b6610fe0845306 --- /dev/null +++ b/public/css/notes.css @@ -0,0 +1,351 @@ +* { + font-family: 'Montserrat', sans-serif; + color: #323643; + background-color: #F7F3F4; + box-sizing: border-box; +} +.navbar{ + position: sticky; + top: 0; + z-index: 100; +} +.main-container { + display: flex; + flex-direction: column; + align-items: center; + padding: 5% 10% 0 10%; + margin: 0 auto; +} + +.searchbar-container { + display: flex; + width: 100%; + flex-direction: row; + margin: 0 auto; + padding: 1.25rem 2% 1.25rem 2%; +} + +.search-box { + display: flex; + width: 80%; + border: 3px solid rgba(50, 54, 67, 0.4); + align-items: center; + justify-content: center; + margin-right: 10px; + border-radius: 20px; + padding: 10px; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); +} + +.search-button { + border-width: 0px; + font-size: 27px; + font-weight: 600; + height: 58px; + margin: 0 10px 0 10px; + transition: opacity 0.2s ease, box-shadow 0.2s ease; /* Efek transisi */ +} + +.search-button:hover { + opacity: 0.7; +} + +.searchTerm { + font-size: 27px; + flex-grow: 1; + border-width: 0px; + width: 50%; + height: 58px; +} + +.searchTerm:focus { + border-width: 0px; + outline: none; /* Menghapus border saat dalam keadaan fokus */ +} + +.tags-button { + border: 3px solid rgba(50, 54, 67, 0.4); + border-radius: 20px; + display: flex; + align-items: center; + font-size: 24px; + padding: 10px; + margin-left: 10px; + font-weight: 600; + height: 50px; + transition: transform 0.2s ease, box-shadow 0.2s ease; /* Efek transisi */ +} + +#sort-button { + border: 3px solid rgba(50, 54, 67, 0.4); + border-radius: 20px; + display: flex; + align-items: center; + font-size: 24px; + padding: 10px; + margin-left: 10px; + font-weight: 600; + height: 50px; + transition: transform 0.2s ease, box-shadow 0.2s ease; /* Efek transisi */ +} + +.note-button { + border-width: 0px; + border-radius: 20px; + background-color: #323643; + color: #F7F3F4; + font-size: 30px; + font-weight: 600; + min-width: 150px; + height: 78px; + transition: transform 0.2s ease, box-shadow 0.2s ease; /* Efek transisi */ +} + +.note-button:hover, +.tags-button:hover, +#sort-button:hover, +.notes-card:hover { + transform: scale(1.05); /* Memperbesar tombol saat dihover */ + box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.1); /* Efek bayangan saat dihover */ +} + + +.tags-list-container { + display: none; + flex-direction: column; + height: auto; + max-height: 200px; + overflow-y: auto; + border: 1px solid #323643; + border-radius: 10px; +} + +.notes-container { + width: 80%; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + margin: 0 auto; + padding: 1.25rem 0 1.25rem 0; +} + +#notes-cards-container { + width: 100%; + height: 100%; + display: flex; + flex-wrap: wrap; + flex-direction: row; +} + +.notes-card { + display: flex; + flex-direction: column; + width: 45%; + /* flex-grow: 1; */ + height: 300px; + background-color: #F7F3F4; + margin: 3% 2% 3% 2%; + padding: 3% 5% 3% 5%; + border-radius: 20px; + border: 3px solid #323643; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + transition: transform 0.2s ease, box-shadow 0.2s ease; /* Efek transisi */ +} + +.note-thumbnail { + display: flex; + width: 60%; + height: 60%; + justify-content: center; + align-items: center; + margin: 0 auto 20px auto; +} + +.notes-information { + width: 100%; + overflow: hidden; +} + +.note-thumbnail-img { + width: auto; + max-width: 100%; + max-height: 100%; +} + +.horizontal-line { + border-bottom: 1px solid #323643; + border-radius: 2px; +} + +.title { + font-size: 24px; + background-color: #F7F3F4; + font-weight: 600; +} + +.tags { + display: flex; + width: 100%; + flex-wrap: wrap; +} + +.tag { + width: 100px; + font-size: 18px; + font-weight: 500; + margin: 5px 10px 5px 10px; + background-color: #323643; + color: #F7F3F4; + border-radius: 10px; + text-align: center; +} + +.notes-pagination { + display: flex; + flex-direction: row; + justify-content: center; +} + +.pagination-number { + border: 5px solid #323643; + color: #323643; + background-color: #F7F3F4; +} + +.pagination-cont { + width: 100%; + display: flex; + flex-direction: row; + justify-content: center; +} + +.page-text { + font-size: 18px; + font-weight: 600; + margin-right: 10px; +} + +.active-pagination-number { + border: 5px solid #323643; + color: #F7F3F4; + background-color: #323643; +} + + +.active-pagination-number, .pagination-number { + display: flex; + font-size: 27px; + border-radius: 10px; + justify-content: center; + align-items: center; + margin: 0 10px 0 10px; + width: 75px; + height: 75px; +} + +.dots { + display: flex; + align-items: end; + font-size: 33px; + font-weight: 700; + color: #323643; +} + +/* CSS untuk layar kecil (misalnya, ukuran layar maksimum 600px) */ +@media screen and (max-width: 600px) { + .search-box { + padding: 5px; + width: 100%; + margin: 5px 0; + } + + .searchbar-container { + display: flex; + width: 100%; + flex-direction: column; + } + + .search-button { + border-width: 0px; + font-size: 27px; + font-weight: 600; + height: 30px; + margin: 0 4px; + } + + .searchTerm { + width: 100%; /* Mengisi lebar 100% saat di bawah search-box */ + height: 40px; + font-size: 20px; + } + + .note-button-cont { + width: 100%; + } + + .note-button { + margin: 5px 0; + width: 100%; /* Mengisi lebar 100% */ + height: 50px; + border-radius: 15px; + } + + .tags-button { + border: 2px solid rgba(50, 54, 67, 0.4); + border-radius: 15px; + font-size: 20px; + font-weight: 600; + height: 50px; + width: 180px; + } + + #sort-button { + border: 2px solid rgba(50, 54, 67, 0.4); + border-radius: 15px; + font-size: 20px; + font-weight: 600; + height: 50px; + width: 180px; + } + + .notes-pagination { + display: flex; + flex-direction: row; + justify-content: center; + } + + .pagination-number { + border: 3px solid #323643; + color: #323643; + background-color: #F7F3F4; + } + + .active-pagination-number { + border: 3px solid #323643; + color: #F7F3F4; + background-color: #323643; + } + + .active-pagination-number, .pagination-number { + display: flex; + font-size: 20px; + border-radius: 10px; + justify-content: center; + align-items: center; + margin: 0 10px 0 10px; + width: 50px; + height: 50px; + } + + .dots { + font-size: 27px; + font-weight: 700; + color: #323643; + } + + .notes-card { + width: 100%; + } + +} diff --git a/public/css/profilpage.css b/public/css/profilpage.css new file mode 100644 index 0000000000000000000000000000000000000000..29831282bae9af23e0406afc6305a7f8076893aa --- /dev/null +++ b/public/css/profilpage.css @@ -0,0 +1,159 @@ +/* Import Google font - Open Sans */ +@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&display=swap'); +* { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: 'Open Sans', sans-serif; +} +body { + background: #F7F3F4; +} +.container { + display: flex; + flex-direction: column; + gap: 35px; + padding: 30px; +} +.page-data { + width: 100%; +} +.page-data .card-page { + color: #fff; + background: #F7F3F4; + border-radius: 5px; + padding: 20px 70px 20px 20px; + display: flex; + justify-content: space-between; + border: solid #606470; + box-shadow: #606470; +} + +.notes .notes-reminder-card{ + display: flex; + gap: 20px; +} + +.notes h2 { + margin: 20px 0; +} + +.reminder h2 { + margin: 20px 0; +} + +.reminder .notes-reminder-card{ + display: flex; + gap: 20px; +} + +.page-data h6 { + margin-top: 12px; + font-size: 1rem; + font-weight: 500; + color: #606470; +} + +.notes-reminder-card .card { + color: #fff; + padding: 18px 16px; + list-style: none; + width: calc(100% / 5); + border-radius: 5px; +} + +.card-image{ + position: relative; + height:100px; + width: 100px; + border-radius: 50%; + background-color: #FFF; + padding:3px; +} + +.card-image .card-img { + height: 100%; + width: 100%; + object-fit: cover; + border-radius: 50%; + border: 4px solid #232323; +} + +.input-field{ + width: 100%; +} + +.input-field input{ + width: 100%; + height: 100%; + padding: 10px; + border-radius: 20%; + border: 2px solid #323643; + box-shadow:0 0 15px 4px rgba(0,0,0,0.06); + border-radius:10px; +} + +.header-box{ + font-style: normal; + color: #232323; + font-weight: 150; + margin:10px 0; +} + +.submit-btn{ + min-width: 120px; + background-color: #323643; + color: white; + border: none; + border-radius: 10px; + box-shadow: 0px 0px 2px 2px rgb(0,0,0); +} + +.wrap-btn{ + margin-left: 20px; +} + +.notes-reminder-card, +.wrap-btn{ + display: flex; +} + +.submit-btn:hover { + background-color:#C70000; + transition: 0.7s; +} + +@media (max-width: 1400px) { + .page-data .card-page { + padding: 20px; + } + .notes-reminder-card { + flex-wrap: wrap; + } + .notes-reminder-card .card { + width: calc(100% / 4 - 15px); + } +} +@media (max-width: 1200px) { + .notes-reminder-card .card { + width: calc(100% / 3 - 15px); + } +} +@media (max-width: 950px) { + .notes-reminder-card .card { + width: calc(100% / 2 - 10px); + } +} +@media (max-width: 750px) { + h1 { + font-size: 1.45rem; + padding: 16px 0; + } + .container { + flex-wrap: wrap; + padding: 15px; + } + .page-data h2 { + font-size: 1.35rem; + } +} \ No newline at end of file diff --git a/public/css/reminder.css b/public/css/reminder.css new file mode 100644 index 0000000000000000000000000000000000000000..f664a7b2451af80c551095b46b80fd20ac3847fd --- /dev/null +++ b/public/css/reminder.css @@ -0,0 +1,336 @@ +@import url('https://fonts.googleapis.com/css?family=Montserrat&display=swap'); + +.html, body{ + background-color: #F7F3F4; +} +.text{ + font-family: Montserrat; +} +.flex-row{ + position: relative; + display: flex; +} +.flex-column{ + position: relative; + display: flex; + flex-direction: column; +} +.reminder .h2{ + font-size: 30px; +} +.reminder .semi-bold{ + font-weight: 500; +} +.overflow-x{ + overflow-x: auto; +} +.overflow-y{ + overflow-y: auto; +} +.box{ + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; +} +.align-bottom{ + margin-top: auto; +} +.align-center{ + justify-content: center; + align-items: center; +} +.navbar{ + position: sticky; + top: 0; + z-index: 2; +} +.reminder{ + box-sizing: border-box; + background-color: #F7F3F4; +} +.reminder .search{ + padding-top: 60px; + gap: 20px; +} +.reminder .search-1{ + width: 700px; + height: 50px; + gap:15px; + padding-right:15px; +} +.reminder .time-filter, +.reminder .category-filter{ + height: 30px; + transition: transform 0.3s ease, box-shadow 0.2s ease; /* Efek transisi untuk scaling */ +} +input[type="text"]{ + border: none; + background: none; + outline: none; + width: 100%; +} +.reminder .searchbar{ + width: 450px; + height: 40px; + position: relative; + display: flex; + justify-content: left; + align-items: center; + padding: 20px; +} +.reminder .time-filter{ + width: 80px; + padding:0px 5px; + gap:10px; +} +.reminder .category-filter{ + width: 120px; + padding:0px 5px; + gap:10px; +} +.reminder .add-button{ + width: 200px; + height: 50px; + background-color: #323643; + border: 0px; + color: #F7F3F4; + padding:0px 5px; + gap:10px; + font-size: 25px; + transition: transform 0.3s ease, box-shadow 0.2s ease; /* Efek transisi untuk scaling */ +} +.reminder .reminders-content { + position: relative; + display: grid; + width: 950px; + margin-top: 40px; + grid-template-rows: repeat(3, 1fr) minmax(30px, auto); + gap: 30px; +} +.reminder .reminder-row{ + position: relative; + display: flex; + gap: 60px; +} +.reminder .reminder-item{ + width: 250px; + height: 100px; + position: relative; + display: flex; + flex-direction: column; + padding: 10px 10px; + font-size: 13px; + align-items: start; + gap:2px; + transition: transform 0.3s ease, box-shadow 0.2s ease; /* Efek transisi untuk scaling */ +} + +.reminder .add-button:hover, +.reminder .category-filter:hover, +.reminder .time-filter:hover, +.reminder .reminder-item:hover { + transform: scale(1.05); /* Memperbesar tombol saat dihover */ + box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.1); /* Efek bayangan saat dihover */ +} + +.reminder .content-title{ + font-size: 15px; + font-weight: bold; +} +.reminder .content-text{ + margin: 2px; +} +.reminder .deadline-category-box{ + gap:10px; +} +.reminder .content-deadline{ + font-size: 10.5px; + padding: 1px 6px; + box-shadow: 0px 0px 0px #00000040; +} +.reminder .content-category{ + width: 130px; + padding: 1px 6px; + text-align: center; + color: #ffffff; + box-sizing: border-box; + background-color: #323643; + border-radius: 20px; +} +.reminder .popup{ + display: none; + position: fixed; + width: 400px; + height: 320px; + top: 50%; + left: 50%; + padding: 20px 20px; + gap: 20px; + transform: translate(-50%, -50%); + align-items: start; + justify-content:unset; + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; + z-index: 4; +} +.reminder .popup-title{ + width: 230px; + margin-left: 20px; +} +.reminder .edit-icon{ + gap: 15px; +} +.reminder .popup-task{ + margin-left: 20px; +} +.reminder .popup-category{ + gap:5px; + margin-right: 30px; +} +.reminder .popup-deadline{ + gap:5px; +} +.reminder .popup-text{ + max-height: 80px; +} +.reminder .popup-category-deadline, +.reminder .popup-text{ + margin-left: 58px; +} +.reminder .icon-popup{ + width: 30px; + height: 30px; + object-fit: cover; +} +.reminder .popup-cover { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #323643; + opacity: 0.4; + z-index: 3; +} +.reminder .category-list { + display: none; + position: relative; + background-color: #fff; + border: 1px solid #ccc; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); + z-index: 1; +} +.reminder .category{ + position: relative; + background-color: #fff; + border: 1px solid #ccc; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); +} +.reminder .category:hover { + background-color: #93DEFF; +} + +.reminder .category-list.active { + display: flex; +} + +.reminder .popup-title-input{ + display: none; + width: 200px; + padding: 5px; + font-size: 20px; + margin-left: 20px; +} +.reminder .popup-text-input{ + display: none; + width: 320px; + padding: 5px; + font-size: 14px; + max-height: 60px; + margin-left: 58px; +} +.reminder .content-deadline-input{ + display: none; + max-width: 160px; + padding: 1px 6px; + box-shadow: 0px 0px 0px #00000040; +} +.reminder .content-category-input{ + display: none; + width: 130px; + padding: 1px 6px; + text-align: center; + color: #ffffff; + box-sizing: border-box; + background-color: #323643; + border-radius: 20px; +} +.reminder .category-list-input { + display: none; + position: absolute; + top:38%; + left:48%; + background-color: #fff; + border: 1px solid #ccc; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); + z-index: 10; +} +.reminder .category-list-input.active { + display: flex; +} +.reminder .submit { + display: none; + position: relative; + padding: 10px; + margin-left: 325px; + background-color: #93DEFF; +} +.reminder .add { + display: none; + position: relative; + padding: 10px; + margin-left: 325px; + background-color: #93DEFF; +} +.reminder .popup-alert{ + display: none; + position: fixed; + width: 300px; + height: 220px; + top: 50%; + left: 50%; + padding: 20px 20px; + gap: 20px; + transform: translate(-50%, -50%); + align-items: start; + justify-content:unset; + background-color: #f6f2f3; + border-radius: 20px; + border: 3px solid; + border-color: #32364366; + box-shadow: 0px 4px 4px #00000040; + z-index: 4; +} +.reminder .fa-triangle-exclamation{ + font-size: 90px; + color: rgb(224, 220, 62) +} +.reminder .confirm-buttons{ + gap: 50px; +} +.hapus{ + font-size: 20px; + color: white; + background-color: red; + padding: 15px; +} +.batal{ + font-size: 20px; + padding: 15px; +} \ No newline at end of file diff --git a/public/css/signin.css b/public/css/signin.css new file mode 100644 index 0000000000000000000000000000000000000000..dc8b1da75de160f8813a43e873d744d6dc649d0a --- /dev/null +++ b/public/css/signin.css @@ -0,0 +1,97 @@ +*{ + padding: 0; + margin: 0; + box-sizing: border-box; + font-family: sans-serif; +} + +body{ + display: flex; + height: 100vh; + justify-content: center; + align-items: center; + background: url(bg-image.jpg); + background-size: cover; +} + +.container{ + width: 100%; + max-width: 650px; + background: #323643; + padding: 28px; + margin: 0 28px; + border-radius: 10px; + box-shadow: inset -2px 2px 2px white; +} + +.form-title{ + font-size: 26px; + font-weight: 600; + text-align: center; + padding-bottom: 6px; + color: white; + text-shadow: 2px 2px 2px black; + border-bottom: solid 1px white; +} + +.main-user-info{ + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 20px 0; +} + +.user-input-box:nth-child(2n){ + justify-content: end; +} + +.user-input-box{ + display: flex; + flex-direction: column; + justify-content: space-between; + padding-bottom: 15px; +} + +.user-input-box input{ + height: 40px; + width: 95%; + border-radius: 7px; + outline: none; + border: 1px solid grey; + padding: 0 10px; +} + + + +.form-submit-btn{ + margin-top: 40px; +} + +.form-submit-btn input{ + display: block; + width: 100%; + margin-top: 10px; + font-size: 20px; + padding: 10px; + border:none; + border-radius: 3px; + color: rgb(255, 255, 255); + background: rgba(135, 107, 221, 0.7); +} + +.form-submit-btn input:hover{ + background: rgba(78, 72, 72, 0.7); + color: rgb(255, 255, 255); +} +.signup{ + position: relative; + display: flex; + color: rgb(255, 255, 255); + gap: 20px; + margin-top: 20px; + text-align: center; + justify-content: center; +} +.signup-text:hover{ + color:rgba(135, 107, 221, 0.7); +} diff --git a/public/css/signup.css b/public/css/signup.css new file mode 100644 index 0000000000000000000000000000000000000000..ba09280fe2d25900b4de246f1bd0fcbf195b161d --- /dev/null +++ b/public/css/signup.css @@ -0,0 +1,85 @@ +*{ + padding: 0; + margin: 0; + box-sizing: border-box; + font-family: sans-serif; +} + +body{ + display: flex; + height: 100vh; + justify-content: center; + align-items: center; + background: url(bg-image.jpg); + background-size: cover; +} + +.container{ + width: 100%; + max-width: 650px; + background: #323643; + padding: 28px; + margin: 0 28px; + border-radius: 10px; + box-shadow: inset -2px 2px 2px white; +} + +.form-title{ + font-size: 26px; + font-weight: 600; + text-align: center; + padding-bottom: 6px; + color: white; + text-shadow: 2px 2px 2px black; + border-bottom: solid 1px white; +} + +.main-user-info{ + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 20px 0; +} + +.user-input-box:nth-child(2n){ + justify-content: end; +} + +.user-input-box{ + display: flex; + flex-direction: column; + justify-content: space-between; + padding-bottom: 15px; +} + +.user-input-box input{ + height: 40px; + width: 95%; + border-radius: 7px; + outline: none; + border: 1px solid grey; + padding: 0 10px; +} + + + +.form-submit-btn{ + margin-top: 40px; +} + +.form-submit-btn input{ + display: block; + width: 100%; + margin-top: 10px; + font-size: 20px; + padding: 10px; + border:none; + border-radius: 3px; + color: rgb(255, 255, 255); + background: rgba(135, 107, 221, 0.7); +} + +.form-submit-btn input:hover{ + background: rgba(78, 72, 72, 0.7); + color: rgb(255, 255, 255); +} diff --git a/public/css/updateprofile.css b/public/css/updateprofile.css new file mode 100644 index 0000000000000000000000000000000000000000..400a4f3b64e5404c3100652d8721263af255ed80 --- /dev/null +++ b/public/css/updateprofile.css @@ -0,0 +1,89 @@ +*{ + padding: 0; + margin: 0; + box-sizing: border-box; + font-family: sans-serif; +} + +body{ + display: flex; + height: 100vh; + justify-content: center; + align-items: center; + background-size: cover; +} + +.container{ + width: 100%; + max-width: 650px; + background: #323643; + padding: 28px; + margin: 0 28px; + border-radius: 10px; + box-shadow: inset -2px 2px 2px white; +} + +.form-title{ + flex:1; + font-size: 26px; + font-weight: 600; + text-align: center; + padding-bottom: 6px; + color: white; + text-shadow: 2px 2px 2px black; +} + +.main-user-info{ + display: flex; + flex-direction: column; + justify-content:center; + +} + +.user-input-box:nth-child(2n){ + justify-content: end; +} + +.user-input-box{ + padding-bottom: 15px; +} + +.user-input-box label{ + color: white; + font-size: 20px; + font-weight: 400; + margin: 5px 0; +} + +.user-input-box input{ + height: 40px; + width: 95%; + border-radius: 7px; + outline: none; + border: 1px solid grey; + padding: 0 10px; + +} + + + +.form-submit-btn{ + margin-top: 40px; +} + +.form-submit-btn input{ + display: block; + width: 100%; + margin-top: 10px; + font-size: 20px; + padding: 10px; + border:none; + border-radius: 3px; + color: rgb(255, 255, 255); + background: rgba(135, 107, 221, 0.7); +} + +.form-submit-btn input:hover{ + background: rgba(78, 72, 72, 0.7); + color: rgb(255, 255, 255); +} diff --git a/public/images/profile.png b/public/images/profile.png new file mode 100644 index 0000000000000000000000000000000000000000..1c927a4f2f4ed48d50493ff0dd74f1cfdb6e44df Binary files /dev/null and b/public/images/profile.png differ diff --git a/public/images/xmark.svg b/public/images/xmark.svg new file mode 100644 index 0000000000000000000000000000000000000000..73cf9513fe428d04deca3652c508c47dfd40b494 --- /dev/null +++ b/public/images/xmark.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"/></svg> \ No newline at end of file diff --git a/public/js/admin.js b/public/js/admin.js new file mode 100644 index 0000000000000000000000000000000000000000..347f01907fe53c5f571b20228f21b41ef0627318 --- /dev/null +++ b/public/js/admin.js @@ -0,0 +1,101 @@ +const fillUserTable = () => { + const xhr = new XMLHttpRequest(); + xhr.open("GET", '../../app/api/admin/getUserInfos.php', true); + + xhr.onload = function () { + if (this.status == 200) { + let response = JSON.parse(this.responseText); + // let userTableCont = document.getElementsByClassName("user-table-container"); + const userTableCont = document.querySelector(".user-table-container"); + userTableCont.innerHTML = ` + <div class="table-header-row-container"> + <div class="user-id"> + User ID + </div> + <div class="username"> + Username + </div> + <div class="email"> + Email + </div> + <div class="notes"> + Notes + </div> + <div class="reminder"> + Reminders + </div> + <div class="edit"> + Edit + </div> + <div class="delete"> + Delete + </div> + </div> + ` + if (response["status"] == "error") { + userTableCont.innerHTML = ` + <div class="bino-sed-container" id="bino-sed-container"> + // <img + // src="../../../public/img/bino-sed.jpg" + // alt="logo" + // class="not-found-pic" + // /> + <p>Sorry, no user found :(</p> + </div>`; + } else { + response.forEach((user) => { + userTableCont.innerHTML += ` + <div class="row-container"> + <div class="id-col-container"> + ${user.user_id} + </div> + <div class="username-col-container"> + ${user.username} + </div> + <div class="email-col-container"> + ${user.email} + </div> + <a href="/?admin/searchnotes/${user.user_id}" class="notes-col-container"> + Here + </a> + <a href="/?admin/reminder/${user.user_id}" class="reminder-col-container"> + Here + </a> + <a href="/?admin/edituser/${user.user_id}" class="edit-col-container"> + <i class="fa-solid fa-pen"></i> + </a> + <button onclick="deleteUser(${user.user_id})" class="delete-col-container"> + <i class="fa-solid fa-trash"></i> + </button> + </div> + `; + }); + } + } else { + alert("failed to get user informations"); + } + }; + xhr.send(); +}; + +const deleteIser = (userId) => { + const data = new FormData(); + data.append("id", userId); + + const xhr = new XMLHttpRequest(); + xhr.open("POST", "../../app/api/admin/delete.php", true); + xhr.onload = function () { + if (xhr.readyState == 4 && xhr.status == 200) { + if (xhr.status == 200) { + alert("User removed"); + + // Arahkan pengguna ke URL /notes + window.location.href = "/?admin/"; + } + } else { + alert('Failed to delete user'); + } + } + + xhr.send(data); +} diff --git a/public/js/navbar.js b/public/js/navbar.js new file mode 100644 index 0000000000000000000000000000000000000000..e011c956f8932fc516db1e11b9cfcb05af3791ee --- /dev/null +++ b/public/js/navbar.js @@ -0,0 +1,15 @@ +function setCursorPointer(element) { + element.style.cursor = "pointer"; +} + +function resetCursor(element) { + element.style.cursor = "default"; +} +function toggleDropdown() { + var dropdownContent = document.getElementById("dropdown-content"); + if (dropdownContent.style.display === "flex") { + dropdownContent.style.display = "none"; + } else { + dropdownContent.style.display = "flex"; + } +} diff --git a/public/js/notecontent.js b/public/js/notecontent.js new file mode 100644 index 0000000000000000000000000000000000000000..0085ebd5bab6f70fcccc077b3a3b099d3ec50bdc --- /dev/null +++ b/public/js/notecontent.js @@ -0,0 +1,77 @@ +function backContent () { + window.history.back(); +} + +function editNote(noteId, isAdmin) { + if (isAdmin) { + window.location.href = "/?admin/editnote/" + noteId; + } else { + window.location.href = "/?notes/edit/" + noteId; + } +}; + +// Fungsi untuk mengisi informasi catatan +function fillNoteContent(data) { + const noteTitle = document.querySelector('.note-title'); + const noteContent = document.querySelector('.note-content'); + const tags = document.querySelector('.tags'); + const video = document.querySelector('video'); + + // Isi judul catatan + noteTitle.textContent = data.title; + + // Isi isi catatan (content) + noteContent.innerHTML = data.content; + + // Isi tag-tag catatan + tags.innerHTML = ''; + + data.tags.forEach((tag) => { + const tagElement = document.createElement('div'); + tagElement.classList.add('tag'); + tagElement.textContent = tag.tag_name; + tags.appendChild(tagElement); + }); + + // Isi video catatan + if (data.video_path) { + video.src = data.video_path; + video.style.display = 'block'; + } else { + video.style.display = 'none'; + } + + // Memuat video baru + video.load(); +} + +// Fungsi untuk mendapatkan informasi catatan dari server +function getNoteContent(noteId) { + const xhr = new XMLHttpRequest(); + const requestURL = '../../app/api/notes/detail.php?id=' + noteId; + xhr.open('GET', requestURL, true); + + xhr.onload = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + const responseData = JSON.parse(xhr.responseText); + fillNoteContent(responseData); + } else { + alert('Failed to fetch note content'); + } + } + }; + + xhr.send(); +} +function setCursorPointer(element) { + element.style.cursor = "pointer"; +} + +function resetCursor(element) { + element.style.cursor = "default"; +} + +// // Panggil fungsi getNoteContent saat halaman dimuat +// const noteId = <?php echo $data['id']; ?>; +// getNoteContent(noteId); diff --git a/public/js/noteedit.js b/public/js/noteedit.js new file mode 100644 index 0000000000000000000000000000000000000000..0abc398df7535231bffc61d86f2daa5283940230 --- /dev/null +++ b/public/js/noteedit.js @@ -0,0 +1,162 @@ +const backEdit = (noteId) => { + window.history.back(); +}; + +function toggleChooseTag() { + // Mengubah tampilan container + let tagsListContainer = document.querySelector('.tags-list-container'); + tagsListContainer.innerHTML + if (tagsListContainer.style.display == 'none') { + tagsListContainer.style.display = 'flex'; + } else { + tagsListContainer.style.display = 'none'; + } + + // // Menghentikan penyebaran event klik ke atas + // event.stopPropagation(); +} + +let tag = "" +function chooseTag(chosenTag) { + let tagsCont = document.getElementById("tags-cont"); + tag = chosenTag + tagsCont.innerHTML = + ` + <div class="tag"> + ${chosenTag} + </div> + + ` + + toggleChooseTag(); +} + +function setCursorPointer(element) { + element.style.cursor = "pointer"; +} + +function resetCursor(element) { + element.style.cursor = "default"; +} + +// Fungsi untuk mengisi input dengan data catatan dari server +function fillNoteData(noteData) { + const noteTitleInput = document.querySelector('.text-input'); + const noteContentTextarea = document.querySelector('.textarea-input'); + const uploadedThumbnailImg = document.getElementById('uploaded-thumbnail-img'); + const uploadedVideoVidCont = document.getElementById('uploaded-video-vid-cont'); + + // Sembunyikan elemen div yang menunjukkan default thumbnail dan video + const uploadedThumbnailDiv = document.getElementById('uploaded-thumbnail-div'); + const uploadedVideoDiv = document.getElementById('uploaded-video-div'); + + let tagsCont = document.getElementById("tags-cont"); + tagsCont.innerHTML = + ` + <div class="tag"> + ${noteData.tags[0].tag_name} + </div> + ` + + uploadedThumbnailDiv.style.display = 'none'; + uploadedVideoDiv.style.display = 'none'; + + uploadedThumbnailImg.style.display = 'flex'; + uploadedVideoVidCont.style.display = 'flex'; + + // Isi input dengan data dari catatan + noteTitleInput.value = noteData.title; + noteContentTextarea.value = noteData.content; + uploadedThumbnailImg.src = noteData.image_path; + uploadedVideoVidCont.src = noteData.video_path; + + uploadedVideoVidCont.load(); + // uploadedThumbnailImg.load(); +} + +// Fungsi untuk mengambil data catatan dari server menggunakan AJAX +function getNoteContent(noteId) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', '../../app/api/notes/detail.php?id=' + noteId, true); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + const noteData = JSON.parse(xhr.responseText); + fillNoteData(noteData); + } + }; + xhr.send(); +} + +// // Panggil fungsi getNoteContent saat halaman dimuat +// window.addEventListener('load', function () { +// const noteId = <?php echo $data['id']; ?>; +// getNoteContent(noteId); +// }); + +// Fungsi untuk menyimpan data yang telah diedit +function saveEditedNote(noteId, userId, isAdmin=0) { + // Ambil nilai dari input teks dan textarea + const noteTitleInput = document.querySelector('.text-input'); + const noteContentTextarea = document.querySelector('.textarea-input'); + const noteTitle = noteTitleInput.value; + const noteContent = noteContentTextarea.value; + + // Buat objek FormData + const data = new FormData(); + + // Ambil file thumbnail yang diunggah + const thumbnailUploadInput = document.getElementById('thumbnail-upload'); + if (thumbnailUploadInput.files.length > 0) { + const thumbnailFile = thumbnailUploadInput.files[0]; + data.append('file', thumbnailFile); + } + + // Ambil file video yang diunggah + const videoUploadInput = document.getElementById('video-upload'); + if (videoUploadInput.files.length > 0) { + const videoFile = videoUploadInput.files[0]; + data.append('file2', videoFile); + } + + // Tambahkan data ke objek FormData + data.append('id', noteId); + data.append('title', noteTitle); + data.append('content', noteContent); + if (tag != "") { + data.append('tags', tag); + } + + const xhr = new XMLHttpRequest(); + xhr.open('POST', '../../app/api/notes/edit.php', true); + // xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + + xhr.onload = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + alert('Note edited successfully'); + // Redirect ke halaman /notes setelah penyimpanan berhasil + if (isAdmin) { + window.location.href = "/?admin/notecontent/" + noteId; + // window.history.back(); + } else { + window.location.href = '/?notes'; + } + + } else { + alert('Failed to edit note'); + location.reload(); + } + } + }; + + xhr.send(data); +} + +function setCursorPointer(element) { + element.style.cursor = "pointer"; +} + +function resetCursor(element) { + element.style.cursor = "default"; +} \ No newline at end of file diff --git a/public/js/noteupload.js b/public/js/noteupload.js new file mode 100644 index 0000000000000000000000000000000000000000..cafeef2f68800fdc27a699ee6cef3eefa35a03f5 --- /dev/null +++ b/public/js/noteupload.js @@ -0,0 +1,118 @@ +const backSave = () => { + window.history.back(); +}; + +function toggleChooseTag() { + // Mengubah tampilan container + let tagsListContainer = document.querySelector('.tags-list-container'); + tagsListContainer.innerHTML + if (tagsListContainer.style.display == 'none') { + tagsListContainer.style.display = 'flex'; + } else { + tagsListContainer.style.display = 'none'; + } +} + +let tag = "" +function chooseTag(chosenTag) { + let tagsCont = document.getElementById("tags-cont"); + tag = chosenTag + tagsCont.innerHTML = + ` + <div class="tag"> + ${chosenTag} + </div> + + ` + + toggleChooseTag(); +} + +function setCursorPointer(element) { + element.style.cursor = "pointer"; +} + +function resetCursor(element) { + element.style.cursor = "default"; +} + +// Fungsi untuk menyimpan catatan +function saveNote(userId=9, isAdmin=1) { +// function saveNote() { + // Buat objek FormData + const formData = new FormData(); + + // Ambil nilai dari input teks dan textarea + const noteTitleInput = document.querySelector('.text-input'); + const noteContentTextarea = document.querySelector('.textarea-input'); + // const tag = document.getElementsByClassName('tag').textContent; + const noteTitle = noteTitleInput.value; + const noteContent = noteContentTextarea.value; + const tags = ["University","Internship"]; + + // Tambahkan data ke objek FormData + formData.append('title', noteTitle); + formData.append('content', noteContent); + // formData.append('user_id', 9); + formData.append('user_id', userId); + // formData.append('tags', "University"); + formData.append('tags', tag); + + // Ambil file thumbnail yang diunggah + const thumbnailUploadInput = document.getElementById('thumbnail-upload'); + if (thumbnailUploadInput.files.length > 0) { + const thumbnailFile = thumbnailUploadInput.files[0]; + formData.append('file', thumbnailFile); + } + + // Ambil file video yang diunggah + const videoUploadInput = document.getElementById('video-upload'); + if (videoUploadInput.files.length > 0) { + const videoFile = videoUploadInput.files[0]; + formData.append('file2', videoFile); + } + + // Buat permintaan XMLHttpRequest + const xhr = new XMLHttpRequest(); + xhr.open('POST', '../../app/api/notes/upload.php', true); + + xhr.onload = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + // Sukses + alert('Note added successfully'); + // Redirect ke halaman /notes setelah penyimpanan berhasil + if (isAdmin) { + window.location.href = "/?admin/searchnotes/" + userId + "/&page=1" ; + } else { + window.location.href = '/?notes'; + } + } else { + // Gagal + alert(xhr.responseText); + // alert('Failed to add note'); + location.reload(); + } + } + }; + + // Kirim objek FormData dalam permintaan + xhr.send(formData); +} + +// // Fungsi yang dipanggil saat tombol "Add Note" ditekan +// function onAddNoteButtonClick() { +// saveNote(); +// window.location.href = "/?notes/"; +// } + +// // Tambahkan event listener ke tombol "Add Note" +// const addNoteButton = document.querySelector('.add-note'); +// addNoteButton.addEventListener('click', onAddNoteButtonClick); +function setCursorPointer(element) { + element.style.cursor = "pointer"; +} + +function resetCursor(element) { + element.style.cursor = "default"; +} \ No newline at end of file diff --git a/public/js/pageprofile.js b/public/js/pageprofile.js new file mode 100644 index 0000000000000000000000000000000000000000..8fd631f294e648c852ac5f01dad98c582f704849 --- /dev/null +++ b/public/js/pageprofile.js @@ -0,0 +1,19 @@ +const usernamePlaceholder = document.getElementById("usernamePlaceholder"); +const emailPlaceholder = document.getElementById("emailPlaceholder"); + + + function getUserInfo() { + const username = prompt("Please enter your username:"); + const email = prompt("Please enter your email:"); + + + if (username) { + usernamePlaceholder.textContent = username; + } + + if (email) { + emailPlaceholder.textContent = email; + } + } + + getUserInfo(); diff --git a/public/js/reminder.js b/public/js/reminder.js new file mode 100644 index 0000000000000000000000000000000000000000..50a7ee25f501a0a708a9c7e1b95746c8dc8e64c6 --- /dev/null +++ b/public/js/reminder.js @@ -0,0 +1,486 @@ +function openPopup(data) { + document.getElementById("popup-title").textContent = data.title; + document.getElementById("popup-text").textContent = data.tasks; + document.getElementById("popup-deadline-2").textContent = data.deadline; + document.getElementById("popup-category-2").textContent = data.category.category_name; + + document.getElementById("popup-cover").style.display = "block"; + document.getElementById("popup").style.display = "flex"; + var confirmationButton = document.getElementById('trash-icon'); + confirmationButton.addEventListener("click", function() { + confirmation(data.reminder_id); + }); + var submitEditButton = document.getElementById('submit'); + submitEditButton.addEventListener("click", function() { + submit(data); + }); + +} + +function confirmation(id){ + document.getElementById("popup-alert").style.display = "flex"; + var deleteButton = document.getElementById('hapus'); + deleteButton.addEventListener("click", function() { + document.getElementById("popup-alert").style.display = "none"; + deleteReminder(id); + }); + var cancelButton = document.getElementById('batal'); + cancelButton.addEventListener("click", function(){ + document.getElementById("popup-alert").style.display = "none"; + }) +} + +function getReminderContent(Id) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', '../../app/api/reminder/detailReminders.php?id=' + Id, true); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + const responseData = JSON.parse(xhr.responseText); + openPopup(responseData); + } else { + alert('Failed to fetch note content'); + } + } + }; + + xhr.send(); +} + + +function closePopup() { + var popup = document.getElementById('popup'); + var popup_cover = document.getElementById('popup-cover'); + popup.style.display = 'none'; + popup_cover.style.display = 'none'; + + document.getElementById("popup-title").style.display = "flex"; + document.getElementById("popup-title-input").style.display = "none"; + document.getElementById("popup-text").style.display = "flex"; + document.getElementById("popup-text-input").style.display = "none"; + document.getElementById("popup-deadline-2").style.display = "flex"; + document.getElementById("popup-deadline-2-input").style.display = "none"; + document.getElementById("popup-category-2").style.display = "flex"; + document.getElementById("popup-category-2-input").style.display = "none"; + + document.getElementById("popup-title-input").contentEditable = "false"; + document.getElementById("popup-text-input").contentEditable = "false"; + document.getElementById("popup-deadline-2-input").contentEditable = "false"; + + document.getElementById("popup-title-input").textContent = ""; + document.getElementById("popup-text-input").textContent = "" ; + document.getElementById("popup-deadline-2-input").value = "" ; + document.getElementById("popup-category-2-input").textContent = "category"; + + document.getElementById("edit-icon").style.display = "flex"; + document.getElementById("trash-icon").style.display = "flex"; + document.getElementById("submit").style.display = "none"; + document.getElementById("add").style.display = "none"; + var icon = document.getElementById("close-icon"); + icon.style.marginLeft = "0px"; +} + +var ascendingOrder = false; +function toggleSort(user_id) { + ascendingOrder = !ascendingOrder; + + var sortIcon = document.getElementById("sortIcon"); + if (ascendingOrder) { + sortIcon.classList.remove("fa-angle-down"); + sortIcon.classList.add("fa-angle-up"); + } else { + sortIcon.classList.remove("fa-angle-up"); + sortIcon.classList.add("fa-angle-down"); + } + + sortReminders(user_id); +} + +function sortReminders(user_id) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', '../../app/api/reminder/sortReminders.php?id=' + user_id, true); + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + const responseData = JSON.parse(xhr.responseText); + sortReminderItemsByDeadline(responseData); + } else { + alert('Failed to fetch note content'); + } + } + }; + + xhr.send(); +} + +function sortReminderItemsByDeadline(data) { + + var dataArray = Object.values(data); + + dataArray.sort(function (a, b) { + var dateA = new Date(a.deadline); + var dateB = new Date(b.deadline); + + if (ascendingOrder) { + return dateA - dateB; + } else { + return dateB - dateA; + } + }); + + var remindersContent = document.querySelector(".reminders-content"); + + while (remindersContent.firstChild) { + remindersContent.removeChild(remindersContent.firstChild); + } + + var numColumns = 3; + var numRows = Math.ceil(dataArray.length / numColumns); + + for (var i = 0; i < numRows; i++) { + var newRow = document.createElement("div"); + newRow.classList.add("reminder-row"); + remindersContent.appendChild(newRow); + + for (var j = 0; j < numColumns; j++) { + var dataIndex = i * numColumns + j; + if (dataIndex < dataArray.length) { + var reminderItem = createReminderItem(dataArray[dataIndex]); + newRow.appendChild(reminderItem); + } + } + } +} +function createReminderItem(reminderData) { + + var reminderItem = document.createElement("div"); + reminderItem.classList.add("reminder-item", "box"); + reminderItem.addEventListener("click", function() { + getReminderContent(reminderData.reminder_id); + }); + reminderItem.addEventListener("mouseover", function() { + setCursorPointer(this); + }); + reminderItem.addEventListener("mouseout", function() { + resetCursor(this); + }); + + var titleElement = document.createElement("div"); + titleElement.classList.add("content-title", "text"); + titleElement.textContent = reminderData.title; + + var contentText = reminderData.tasks.length > 110 ? reminderData.tasks.substring(0, 110) + "..." : reminderData.tasks; + var contentTextElement = document.createElement("p"); + contentTextElement.classList.add("content-text", "text"); + contentTextElement.textContent = contentText; + + var deadlineDate = new Date(reminderData.deadline); + var day = deadlineDate.getDate(); + var month = deadlineDate.toLocaleString("default", { month: "short" }); + var year = deadlineDate.getFullYear(); + var hour = deadlineDate.getHours().toString().padStart(2, "0"); + var minute = deadlineDate.getMinutes().toString().padStart(2, "0"); + + var deadlineDateDisplay = day + " " + month + " " + year + " " + hour + ":" + minute; + var deadlineElement = document.createElement("div"); + deadlineElement.classList.add("content-deadline", "text", "box"); + deadlineElement.textContent = deadlineDateDisplay; + + var categoryElement = document.createElement("div"); + categoryElement.classList.add("content-category"); + categoryElement.textContent = reminderData.category.category_name; + + var deadlineCategoryBox = document.createElement("div"); + deadlineCategoryBox.classList.add("deadline-category-box", "flex-row", "align-bottom", "align-center"); + + reminderItem.appendChild(titleElement); + reminderItem.appendChild(contentTextElement); + deadlineCategoryBox.appendChild(deadlineElement); + deadlineCategoryBox.appendChild(categoryElement); + reminderItem.appendChild(deadlineCategoryBox); + + return reminderItem; +} + +function setCursorPointer(element) { + element.style.cursor = "pointer"; +} + +function resetCursor(element) { + element.style.cursor = "default"; +} + +var searchInput = document.getElementById('searchInput'); +var reminderItems = Array.from(document.querySelectorAll('.reminder-item')); +var remindersContent = document.querySelector('.reminders-content'); +var nowCategory = "All"; + +function runSearch() { + var searchText = searchInput.value.toLowerCase(); + + var matchingItems = []; + count = 0; + + reminderItems.forEach(function (item) { + var title = item.querySelector('.content-title').textContent.toLowerCase(); + var itemCategory = item.querySelector(".content-category").textContent; + if (title.includes(searchText) && (nowCategory==="All" || itemCategory === nowCategory)) { + matchingItems.push(item); + count++; + } else {} + }); + while (remindersContent.firstChild) { + remindersContent.removeChild(remindersContent.firstChild); + } + var numColumns = 3; + + for (var i = 0; i < count; i++) { + var columnIndex = i % numColumns; + + if (columnIndex === 0) { + var newRow = document.createElement("div"); + newRow.classList.add("reminder-row"); + remindersContent.appendChild(newRow); + } + + remindersContent.lastChild.appendChild(matchingItems[i]); + } +} + +var originalReminderItems = Array.from(document.querySelectorAll('.reminder-item')); + +function filterByCategory(category) { + + nowCategory = category; + + document.getElementById("category-title").textContent = category; + + runSearch(); + + var reminderItems = Array.from(document.querySelectorAll('.reminder-item')); + + var remindersContent = document.querySelector('.reminders-content'); + + var matchingItems = []; + count = 0; + + reminderItems.forEach(function(item) { + var itemCategory = item.querySelector(".content-category").textContent; + if (category === "All" || itemCategory === category) { + matchingItems.push(item); + count++; + } + }); + + while (remindersContent.firstChild) { + remindersContent.removeChild(remindersContent.firstChild); + } + + var numColumns = 3; + + for (var i = 0; i < count; i++) { + var columnIndex = i % numColumns; + + if (columnIndex === 0) { + var newRow = document.createElement("div"); + newRow.classList.add("reminder-row"); + remindersContent.appendChild(newRow); + } + + remindersContent.lastChild.appendChild(matchingItems[i]); + } + toggleCategoryList(); +} + +searchInput.addEventListener('input', runSearch); + +function toggleCategoryList() { + var categoryList = document.querySelector(".category-list"); + categoryList.classList.toggle("active"); +} + +function addReminder() { + document.getElementById("popup-title").style.display = "none"; + document.getElementById("popup-title-input").style.display = "flex"; + document.getElementById("popup-text").style.display = "none"; + document.getElementById("popup-text-input").style.display = "flex"; + document.getElementById("popup-deadline-2").style.display = "none"; + document.getElementById("popup-deadline-2-input").style.display = "flex"; + document.getElementById("popup-category-2").style.display = "none"; + document.getElementById("popup-category-2-input").style.display = "flex"; + + document.getElementById("popup-title-input").contentEditable = "true"; + document.getElementById("popup-text-input").contentEditable = "true"; + document.getElementById("popup-deadline-2-input").contentEditable = "true"; + document.getElementById("popup-category-2-input").textContent = "category"; + + + document.getElementById("popup-cover").style.display = "block"; + document.getElementById("edit-icon").style.display = "none"; + document.getElementById("trash-icon").style.display = "none"; + document.getElementById("popup").style.display = "flex"; + document.getElementById("add").style.display = "flex"; + var icon = document.getElementById("close-icon"); + icon.style.marginLeft = "90px"; + +} + +function editReminder() { + document.getElementById("popup-title").style.display = "none"; + document.getElementById("popup-title-input").style.display = "flex"; + document.getElementById("popup-text").style.display = "none"; + document.getElementById("popup-text-input").style.display = "flex"; + document.getElementById("popup-deadline-2").style.display = "none"; + document.getElementById("popup-deadline-2-input").style.display = "flex"; + document.getElementById("popup-category-2").style.display = "none"; + document.getElementById("popup-category-2-input").style.display = "flex"; + + document.getElementById("popup-title-input").textContent = document.getElementById("popup-title").textContent ; + document.getElementById("popup-title-input").contentEditable = "true"; + document.getElementById("popup-text-input").textContent = document.getElementById("popup-text").textContent ; + document.getElementById("popup-text-input").contentEditable = "true"; + document.getElementById("popup-deadline-2-input").value = document.getElementById("popup-deadline-2").textContent ; + document.getElementById("popup-deadline-2-input").contentEditable = "true"; + document.getElementById("popup-category-2-input").textContent = document.getElementById("popup-category-2").textContent; + + document.getElementById("popup-cover").style.display = "block"; + document.getElementById("popup").style.display = "flex"; + document.getElementById("submit").style.display = "flex"; + +} + +const popupTitleInput = document.getElementById("popup-title-input"); + +const maxLength = 16; + +popupTitleInput.addEventListener("keydown", function(event) { + const text = popupTitleInput.textContent; + const length = text.length; + + if (length > maxLength && event.key !== 'Backspace' && event.key !== 'Delete') { + event.preventDefault(); + } +}); + +function toggleCategoryListInput() { + var categoryListInput = document.querySelector(".category-list-input"); + categoryListInput.classList.toggle("active"); +} + + +function filterByCategoryInput(category) { + var popupCategoryInput = document.getElementById("popup-category-2-input"); + popupCategoryInput.textContent = category; + toggleCategoryListInput(); +} + +function add(user_id){ + const title = document.getElementById("popup-title-input").textContent + const tasks = document.getElementById("popup-text-input").textContent + const deadline = document.getElementById("popup-deadline-2-input").value + const category = document.getElementById("popup-category-2-input").textContent + + if (title === "" || tasks === "" || deadline === "" || category === "" || category === "All" || category === "category") { + alert("Please fill all the field"); + return; + } + + const data = new FormData(); + data.append("user_id",user_id ) + data.append("title", title); + data.append("tasks", tasks); + data.append("deadline", deadline); + data.append("category", category); + + const xhr = new XMLHttpRequest(); + xhr.open('POST', '../../app/api/reminder/addReminder.php?id=' + user_id, true); + + xhr.onload = function () { + if (this.status === 200) { + alert("Add reminder success"); + location.reload(); + } else { + alert(this.responseText); + } + }; + + xhr.send(data); + + closePopup(); +} + +function deleteReminder(reminder_id) { + + const xhr = new XMLHttpRequest(); + xhr.open('POST', '../../app/api/reminder/deleteReminder.php?reminder_id=' + reminder_id, true); + + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && xhr.status == 200) { + if (xhr.status == 200) { + alert("Reminder removed"); + location.reload(); + } + } + } + const data = new FormData(); + data.append("reminder_id", reminder_id); + + xhr.send(data); + closePopup(); +} +function submit(data){ + const title = document.getElementById("popup-title-input").textContent + const tasks = document.getElementById("popup-text-input").textContent + const deadline = document.getElementById("popup-deadline-2-input").value + const category = document.getElementById("popup-category-2-input").textContent + + if (title === "" || tasks === "" || deadline === "" || category === "" || category === "All" || category === "category") { + alert("Please fill all the field"); + return; + } + + const newData = new FormData(); + newData.append("reminder_id", data.reminder_id) + newData.append("user_id",data.user_id) + newData.append("title", title); + newData.append("tasks", tasks); + newData.append("deadline", deadline); + newData.append("category", category); + + const xhr = new XMLHttpRequest(); + xhr.open('POST', '../../app/api/reminder/editReminder.php', true); + + xhr.onload = function () { + if (this.status === 200) { + alert("Edit reminder success"); + var submitEditButton = document.getElementById('submit'); + submitEditButton.removeEventListener("click", function() { + submit(data); + }); + location.reload(); + } else { + alert("Edit reminder failed"); + } + }; + + xhr.send(newData); + + closePopup(); +} + + + + + + + + + + + + + + + diff --git a/public/js/removenote.js b/public/js/removenote.js new file mode 100644 index 0000000000000000000000000000000000000000..34c9680c0b7a3f5f434c150da5b0b01478b9cdf3 --- /dev/null +++ b/public/js/removenote.js @@ -0,0 +1,42 @@ +const removeNote = (noteId) => { + const data = new FormData(); + data.append("id", noteId); + + const xhr = new XMLHttpRequest(); + xhr.open("POST", "../../app/api/notes/delete.php", true); + xhr.onload = function () { + if (xhr.readyState == 4 && xhr.status == 200) { + if (xhr.status == 200) { + alert("Note removed"); + // document.getElementById("notelist-row-" + noteId).remove(); + + // Arahkan pengguna ke URL /notes + window.location.href = "/?notes/"; + } + } else { + alert('Failed to delete note'); + } + } + + xhr.send(data); +} + +function confirmation(id){ + document.getElementById("popup-alert").style.display = "flex"; + var deleteButton = document.getElementById('hapus'); + deleteButton.addEventListener("click", function() { + document.getElementById("popup-alert").style.display = "none"; + removeNote(id); + }); + var cancelButton = document.getElementById('batal'); + cancelButton.addEventListener("click", function(){ + document.getElementById("popup-alert").style.display = "none"; + }) +} + +// function onDeleteNoteButtonClick() { +// window.location.href = "/?notes/"; +// } + +// const deleteNoteButton = document.querySelector('.delete-note'); +// deleteNoteButton.addEventListener('click', onDeleteNoteButtonClick); diff --git a/public/js/searchnote.js b/public/js/searchnote.js new file mode 100644 index 0000000000000000000000000000000000000000..fda340842a7038c63f109be27599b051e7105c9d --- /dev/null +++ b/public/js/searchnote.js @@ -0,0 +1,311 @@ +let tag = getQueryVariable("tag") || "" +let sort = getQueryVariable("sort") || "" +let title = getQueryVariable("title") || "" +let page = parseInt(getQueryVariable("page")) || 1; +let searchTimeout; // Variabel untuk menyimpan timeout + +function debounceSearchNote(userId, isAdmin) { + clearTimeout(searchTimeout); + searchTimeout = setTimeout(function () { + searchNote("", userId, isAdmin); + }, 500); +} + +function addNote(userId, isAdmin) { + if (isAdmin) { + window.location.href = "/?admin/addnote/" + userId; + } else { + window.location.href = "/?notes/addnote/"; + } +}; + +function openNote(noteId, isAdmin) { + if (isAdmin) { + window.location.href = "/?admin/notecontent/" + noteId; + } else { + window.location.href = "/?notes/content/" + noteId; + } +} + +function getQueryVariable(variable) { + const query = window.location.search.substring(1); + const vars = query.split('&'); + for (let i = 0; i < vars.length; i++) { + const pair = vars[i].split('='); + if (decodeURIComponent(pair[0]) === variable) { + return decodeURIComponent(pair[1]); + } + } + return ""; +} + +function updateQueryStringParameter(uri, key, value) { + var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i"); + var separator = uri.indexOf("?") !== -1 ? "&" : "?"; + if (uri.match(re)) { + return uri.replace(re, "$1" + key + "=" + value + "$2"); + } else { + return uri + separator + key + "=" + value; + } +} + +const searchNote = (data = "", userId, isAdmin) => { + let base_url = "../../app/api/notes/search.php?"; + let add_url = ""; + query_data = data + + try { + if (document.getElementById("search-title-input")) { + title = document.getElementById("search-title-input").value; + } + + if (title.length > 0) { + add_url += "&title=" + title; + } + if (tag.length > 0) { + add_url += "&tags=" + tag; + } + if (sort) { + add_url += "&sort=" + sort; + } + + if (page.length > 0) { + add_url += "&page=" + page; + } else { + add_url += "&page=1"; + } + listPages(userId, isAdmin) + } catch (e) { + console.log(e); + } + + const xhr = new XMLHttpRequest(); + + if (data != "") { + final_url = base_url + data; + add_url = data; + } else { + final_url = base_url + add_url; + } + + final_url += "&user_id=" + userId; + + xhr.open("GET", final_url, true); + + xhr.onload = function () { + if (this.status == 200) { + let response = JSON.parse(this.responseText); + + let notesCardsCont = document.getElementById("notes-cards-container"); + notesCardsCont.innerHTML = ""; + if (isAdmin) { + window.history.pushState("", "", "/?admin/searchnotes/" + userId + "/" + add_url); + } else { + window.history.pushState("", "", "/?notes/" + add_url); + } + + if (response["status"] == "error") { + notesCardsCont.innerHTML = ` + <div class="bino-sed-container" id="bino-sed-container"> + <p>Sorry, no notes found :(</p> + </div>`; + } else { + response.forEach((note) => { + notesCardsCont.innerHTML += ` + <div class="notes-card" onclick="openNote(${note.note_id}, ${isAdmin})"> + <div class="note-thumbnail"> + <img src="${note.image_path}" alt="note thumbnail" class="note-thumbnail-img"> + </div> + <div class="notes-information"> + <div class="title"> + ${note.title} + </div> + <hr class="horizontal-line"> + + <div class="tags"> + <div class="tag"> + ${note.tag_name} + </div> + </div> + </div> + </div> + `; + }); + } + } else { + if (isAdmin) { + window.location.href = "/?admin/searchnotes/" + userId + "/&page=1" ; + } else { + window.location.href = "/?notes/&page=1" ; + } + alert("failed to search notes"); + } + }; + xhr.send(); +}; + +function toggleSort() { + // Mengubah tampilan container + let sortButton = document.getElementById('sort-button'); + if (sort == 1) { + sort = ""; + sortButton.innerHTML = + `Date + <i class="fa fa-sort-amount-down"></i> + `; + } else { + sort = 1; + sortButton.innerHTML = + `Date + <i class="fa fa-sort-amount-up"></i>`; + } +} + +function toggleChooseTag() { + // Mengubah tampilan container + let tagsListContainer = document.querySelector('.tags-list-container'); + if (tagsListContainer.style.display == 'none') { + tagsListContainer.style.display = 'flex'; + } else { + tagsListContainer.style.display = 'none'; + } +} + +function chooseTag(chosenTag) { + let tagsButton = document.querySelector('.tags-button'); + + if (chosenTag == "All") { + tag = "" + } else { + tag = chosenTag + } + tagsButton.textContent = chosenTag; + + toggleChooseTag(); +} + +function setTitleInput() { + // Dapatkan nilai parameter 'title' dari query string + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const titleParam = urlParams.get('title'); + + // Temukan elemen input 'search-title-input' + const searchTitleInput = document.getElementById('search-title-input'); + + // Periksa apakah 'titleParam' ada dan isi input 'search-title-input' jika ada + if (titleParam) { + searchTitleInput.value = titleParam; + } +} + +function setPageNum() { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + // Dapatkan nilai parameter 'page' dari query string + const pageParam = urlParams.get('page'); + + // Temukan elemen select 'page-dropdown' + const pageDropdown = document.getElementById('page-dropdown'); + + // Periksa apakah 'pageParam' ada dan pilih opsi yang sesuai pada dropdown + if (pageParam) { + // Ubah nilai 'page-dropdown' ke 'pageParam' + pageDropdown.value = pageParam; + } +} + +function listPages(userId=2, isAdmin) { + try { + // Parse query_data menjadi objek URLSearchParams + const queryParams = new URLSearchParams(); + + if (isAdmin) { + if (userId) { + queryParams.set('user_id', userId); + } + } + if (title.length > 0) { + queryParams.set('title', title); + } + if (tag.length > 0) { + queryParams.set('tag', tag); + } + if (page.length > 0) { + queryParams.set('tag', tag); + } + + // Dapatkan string URL yang telah diperbarui + const updatedQuery = queryParams.toString(); + + const xhr = new XMLHttpRequest(); + xhr.open('GET', '../../app/api/notes/page.php?' + updatedQuery, true); + + xhr.onload = function () { + if (xhr.readyState === 4 && xhr.status === 200) { + const countNote = JSON.parse(xhr.responseText); + const totalPages = Math.ceil(countNote.num_note / 4); + + let currentPage = parseInt(getQueryVariable("page")) || 1; + + // Dapatkan elemen dropdown + const pageDropdown = document.getElementById('page-dropdown'); + + // Bersihkan pilihan yang ada jika ada + pageDropdown.innerHTML = ''; + + // Buat pilihan untuk setiap halaman + for (let i = 1; i <= totalPages; i++) { + const option = document.createElement('option'); + option.value = i; + option.textContent = i; + + // Tandai pilihan yang sesuai dengan halaman saat ini + if (i === currentPage) { + option.selected = true; + } + + pageDropdown.appendChild(option); + } + } + setPageNum(); + }; + xhr.send(); + } catch (e) { + console.log(e); + } +} + +// function changePage() { +function changePage(userId=2, isAdmin) { + // Dapatkan nilai halaman yang dipilih dari dropdown + const selectedPage = document.getElementById('page-dropdown').value; + + try { + // Parse query_data menjadi objek URLSearchParams + const queryParams = new URLSearchParams(); + + // Setel nilai parameter 'page' menjadi selectedPage + queryParams.set('page', selectedPage); + if (title.length > 0) { + queryParams.set('title', title); + } + if (tag.length > 0) { + queryParams.set('tag', tag); + } + if (sort) { + queryParams.set('sort', sort); + } + + // Dapatkan string URL yang telah diperbarui + const updatedQuery = queryParams.toString(); + + if (isAdmin) { + window.location.href = "/?admin/searchnotes/" + userId + "/&" + updatedQuery; + } else { + window.location.href = "/?notes/&" + updatedQuery; + } + } catch (e) { + console.log(e); + } +} diff --git a/public/js/useredit.js b/public/js/useredit.js new file mode 100644 index 0000000000000000000000000000000000000000..7572df2e963d53935c01145305d1e904928d929d --- /dev/null +++ b/public/js/useredit.js @@ -0,0 +1,39 @@ +// Fungsi untuk menyimpan perubahan profil pengguna +function saveUserProfile(userId) { + const emailInput = document.querySelector('#email-input'); + const passwordInput = document.querySelector('#password-input'); + + // Ambil nilai input + const newEmail = emailInput.value; + const newPassword = passwordInput.value; + + // Kirim permintaan AJAX untuk menyimpan perubahan profil pengguna + const xhr = new XMLHttpRequest(); + xhr.open('POST', '../../app/api/user/edit.php', false); + // xhr.setRequestHeader('Content-Type', 'application/json'); + + const data = new FormData(); + data.append('user_id', userId); + data.append('email', newEmail); + data.append('password', newPassword); + + xhr.onload = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + alert('Profile updated successfully'); + window.location.href = '/?admin'; // Ganti dengan URL halaman profil pengguna Anda + } else { + console.error(xhr.responseText); + alert('Failed to update profile'); + } + } + }; + + xhr.send(data); +} + +// // Tambahkan event listener untuk tombol "Save" +// const saveButton = document.getElementById('saveButton'); +// saveButton.addEventListener('click', function () { +// saveUserProfile(); +// }); diff --git a/screenshots/Screenshot(2687).png b/screenshots/Screenshot(2687).png new file mode 100644 index 0000000000000000000000000000000000000000..0c2957233b0f9467296e6999eaa912e2f02d423d Binary files /dev/null and b/screenshots/Screenshot(2687).png differ diff --git a/screenshots/Screenshot(2688).png b/screenshots/Screenshot(2688).png new file mode 100644 index 0000000000000000000000000000000000000000..3d4aa608844ae8939449b0e3790d71848b38847a Binary files /dev/null and b/screenshots/Screenshot(2688).png differ diff --git a/screenshots/Screenshot(2689).png b/screenshots/Screenshot(2689).png new file mode 100644 index 0000000000000000000000000000000000000000..30766d9663de957a1ab6e8db62b99cfd6b9bc1e4 Binary files /dev/null and b/screenshots/Screenshot(2689).png differ diff --git a/screenshots/Screenshot(2690).png b/screenshots/Screenshot(2690).png new file mode 100644 index 0000000000000000000000000000000000000000..c7ed1ada0ed0915b940bed7f028e0936efda9606 Binary files /dev/null and b/screenshots/Screenshot(2690).png differ diff --git a/screenshots/Screenshot(2691).png b/screenshots/Screenshot(2691).png new file mode 100644 index 0000000000000000000000000000000000000000..c334be23c7efaf261d2b20ec6c425b90f9a09374 Binary files /dev/null and b/screenshots/Screenshot(2691).png differ diff --git a/screenshots/Screenshot(2692).png b/screenshots/Screenshot(2692).png new file mode 100644 index 0000000000000000000000000000000000000000..520fca86f86eb309f60136c71ee58c713f591bd7 Binary files /dev/null and b/screenshots/Screenshot(2692).png differ diff --git a/screenshots/Screenshot(2693).png b/screenshots/Screenshot(2693).png new file mode 100644 index 0000000000000000000000000000000000000000..e2878a9aa09342c8c201370ffe946e339e0d98cc Binary files /dev/null and b/screenshots/Screenshot(2693).png differ diff --git a/screenshots/Screenshot(2694).png b/screenshots/Screenshot(2694).png new file mode 100644 index 0000000000000000000000000000000000000000..6589e7f9752eacb67f56bfdbdc76e0afb96da3bf Binary files /dev/null and b/screenshots/Screenshot(2694).png differ diff --git a/screenshots/Screenshot(2695).png b/screenshots/Screenshot(2695).png new file mode 100644 index 0000000000000000000000000000000000000000..8367d8e70ebf98172ee15966452d40958ad061c2 Binary files /dev/null and b/screenshots/Screenshot(2695).png differ diff --git a/screenshots/Screenshot(2696).png b/screenshots/Screenshot(2696).png new file mode 100644 index 0000000000000000000000000000000000000000..5dcdb58c5510cc49d7bd162d6a4b3bd00ce40036 Binary files /dev/null and b/screenshots/Screenshot(2696).png differ diff --git a/screenshots/Screenshot(2697).png b/screenshots/Screenshot(2697).png new file mode 100644 index 0000000000000000000000000000000000000000..eb1d5363731db4957e2520185b86d24b5d924ee6 Binary files /dev/null and b/screenshots/Screenshot(2697).png differ diff --git a/scripts/build-image.sh b/scripts/build-image.sh new file mode 100644 index 0000000000000000000000000000000000000000..ce096bac1ad2e7c6eec731197a2b126c245fa237 --- /dev/null +++ b/scripts/build-image.sh @@ -0,0 +1 @@ +docker build -t tubes-1:latest . \ No newline at end of file