From 8fe34f272a235884c65c7700f7f749fdbabe783f Mon Sep 17 00:00:00 2001 From: williamnixon20 <75229742+williamnixon20@users.noreply.github.com> Date: Fri, 6 Oct 2023 15:59:29 +0700 Subject: [PATCH] tidying up frontend css,logic --- public/components/Footer.php | 5 + public/components/Navbar.php | 4 - public/components/Pagination.php | 47 ++- public/css/login.css | 66 +++++ public/css/styles.css | 361 +++++++++++++++++------- public/images/logo.jpeg | Bin 0 -> 4363 bytes public/images/placeholder.png | Bin 0 -> 2181 bytes public/js/login.js | 129 ++++----- public/js/sighting.js | 19 +- public/view/cats.php | 81 +++--- public/view/home.php | 123 ++------ public/view/login.php | 46 +-- public/view/sighting.php | 120 ++++---- public/view/users.php | 5 +- src/bases/BaseRepository.php | 4 +- src/repositories/SightingRepository.php | 92 +++--- 16 files changed, 654 insertions(+), 448 deletions(-) create mode 100644 public/components/Footer.php create mode 100644 public/css/login.css create mode 100644 public/images/logo.jpeg create mode 100644 public/images/placeholder.png diff --git a/public/components/Footer.php b/public/components/Footer.php new file mode 100644 index 0000000..7be83cc --- /dev/null +++ b/public/components/Footer.php @@ -0,0 +1,5 @@ +<footer> + <p>© + <?= date('Y') ?> Kuchenk ITB + </p> +</footer> \ No newline at end of file diff --git a/public/components/Navbar.php b/public/components/Navbar.php index c2c26e9..3e3f0aa 100644 --- a/public/components/Navbar.php +++ b/public/components/Navbar.php @@ -4,12 +4,8 @@ <li><a href="cat">Cats</a></li> <li><a href="sighting">Sightings</a></li> <?php - // Check if the user is logged in (You will need to implement this logic) $loggedIn = isset($_SESSION['user_id']); $isAdmin = isset($_SESSION['isAdmin']) && $_SESSION['isAdmin']; -// echo isset($_SESSION['isAdmin'])===1; -// echo ('<script>console.log('.$isAdmin.')</script>') ; -// echo "<li><a>$loggedIn</a></li>"; if ($isAdmin) { echo '<li><a href="user">Manage Users</a></li>'; } diff --git a/public/components/Pagination.php b/public/components/Pagination.php index 12faf5e..958ac59 100644 --- a/public/components/Pagination.php +++ b/public/components/Pagination.php @@ -4,7 +4,6 @@ // 2. Di controller sebelum render page, query total count dari database simpan di variabel $_count. // 3. Pastiin di service/repo ada fungsi buat ngurusin order by, limit, dst. // For more information, refer to GET /cats di cats controller. - function generatePaginationLinks($currentPage, $totalPages, $queryParams) { $html = '<div class="pagination">'; @@ -12,12 +11,20 @@ function generatePaginationLinks($currentPage, $totalPages, $queryParams) // Build the query string with the existing parameters $queryString = http_build_query($queryParams); + // Calculate the range of visible page links + $visiblePages = 2; // You can adjust this number as needed + // Previous button if ($currentPage > 1) { $html .= '<a href="?pageNo=' . ($currentPage - 1) . '&' . $queryString . '" class="prev-page">Previous</a>'; } - for ($i = 1; $i <= $totalPages; $i++) { + // Display ellipsis if there are many pages before the current page + if ($currentPage > ($visiblePages + 1)) { + $html .= '<span class="ellipsis">...</span>'; + } + + for ($i = max(1, $currentPage - $visiblePages); $i <= min($totalPages, $currentPage + $visiblePages); $i++) { if ($i === $currentPage) { $html .= '<span class="current-page">' . $i . '</span>'; } else { @@ -25,15 +32,49 @@ function generatePaginationLinks($currentPage, $totalPages, $queryParams) } } + // Display ellipsis if there are many pages after the current page + if ($currentPage < ($totalPages - $visiblePages)) { + $html .= '<span class="ellipsis">...</span>'; + } + // Next button if ($currentPage < $totalPages) { - $html .= '<a href="?pageNo=' . ($currentPage + 1) . '&&' . $queryString . '" class="next-page">Next</a>'; + $html .= '<a href="?pageNo=' . ($currentPage + 1) . '&' . $queryString . '" class="next-page">Next</a>'; } $html .= '</div>'; return $html; } +// function generatePaginationLinks($currentPage, $totalPages, $queryParams) +// { +// $html = '<div class="pagination">'; + +// // Build the query string with the existing parameters +// $queryString = http_build_query($queryParams); + +// // Previous button +// if ($currentPage > 1) { +// $html .= '<a href="?pageNo=' . ($currentPage - 1) . '&' . $queryString . '" class="prev-page">Previous</a>'; +// } + +// for ($i = 1; $i <= $totalPages; $i++) { +// if ($i === $currentPage) { +// $html .= '<span class="current-page">' . $i . '</span>'; +// } else { +// $html .= '<a href="?pageNo=' . $i . '&' . $queryString . '">' . $i . '</a>'; +// } +// } + +// // Next button +// if ($currentPage < $totalPages) { +// $html .= '<a href="?pageNo=' . ($currentPage + 1) . '&&' . $queryString . '" class="next-page">Next</a>'; +// } + +// $html .= '</div>'; +// return $html; +// } + $totalCount = $_count; // Total number of cats $pageSize = isset($_GET['pageSize']) ? intval($_GET['pageSize']) : 10; // Number of cats per page $totalPages = ceil($totalCount / $pageSize); diff --git a/public/css/login.css b/public/css/login.css new file mode 100644 index 0000000..6bf0a0c --- /dev/null +++ b/public/css/login.css @@ -0,0 +1,66 @@ + body { + background-color: #f7f7f7; + font-family: Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + margin: 0; + } + + .login-container { + background-color: #fff; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + border-radius: 5px; + padding: 70px; + margin: 20px; + width: 350px; + } + + .login-logo { + text-align: center; + margin-bottom: 20px; + } + + .login-logo img { + max-width: 150px; + } + + .login-title { + text-align: center; + font-size: 24px; + margin-bottom: 20px; + } + + .login-form { + text-align: center; + } + + .login-label { + font-size: 16px; + display: block; + margin-bottom: 5px; + } + + .login-input { + width: 100%; + height: 40px; + border: 1px solid #ccc; + border-radius: 5px; + font-size: 16px; + } + + .login-button { + background-color: #007bff; + color: #fff; + padding: 10px 20px; + border: none; + border-radius: 5px; + font-size: 16px; + cursor: pointer; + transition: background-color 0.3s; + } + + .login-button:hover { + background-color: #0056b3; + } diff --git a/public/css/styles.css b/public/css/styles.css index f39b28d..f090cf5 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -24,7 +24,7 @@ nav ul li a:hover { text-decoration: underline; } -/* Logout button styles (customize as needed) */ +/* Logout button styles*/ .logout-button { background-color: #f00; color: #fff; @@ -33,7 +33,7 @@ nav ul li a:hover { cursor: pointer; } -/* Register and Login link styles (customize as needed) */ +/* Register and Login link styles */ .register-link, .login-link { color: #fff; @@ -41,138 +41,59 @@ nav ul li a:hover { margin-right: 10px; } -/* Apply styles for the logout button, register, and login links */ -/* styles.css */ -/* Style for the cat card container */ -.cat-card, .sighting-card, .user-card { - border: 1px solid #ccc; - padding: 16px; - margin: 16px; - background-color: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - border-radius: 5px; -} -/* Style for the cat image */ -.cat-image { - max-width: 100%; - height: auto; -} - -/* Style for the cat audio player */ .cat-audio { width: 100%; } -/* Style for the cat card title */ .cat-title, .user-card { font-size: 24px; margin: 8px 0; } -/* Style for the cat card details */ + .cat-details, .user-card { font-size: 16px; margin: 8px 0; } -/* styles.css */ - -/* Style for the container that holds the cat cards */ -.cat-cards-container, .sighting-cards-container, .user-cards-container { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); /* Responsive grid with minimum 300px width */ - grid-gap: 16px; /* Spacing between cat cards */ -} - -/* Style for each cat card -.cat-card { - border: 1px solid #ccc; - padding: 16px; - background-color: #fff; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - border-radius: 5px; -} */ - -/* Style for the cat image */ -.cat-image { - max-width: 100%; - height: auto; -} - -/* Style for the cat audio player */ -.cat-audio { - width: 100%; -} - -/* Style for the cat card title */ .cat-title { font-size: 24px; margin: 8px 0; - color: #333; /* Text color */ -} - -/* Style for the cat card details */ -.cat-details { - font-size: 16px; - margin: 8px 0; - color: #777; /* Text color */ + color: #333; } -/* styles.css */ - -/* ... Previous styles ... */ -/* Style for the cat description */ .cat-description { font-size: 14px; margin: 8px 0; - color: #555; /* Text color */ + color: #555; } -/* Style for the cat location */ .cat-location { font-size: 14px; margin: 8px 0; - color: #555; /* Text color */ + color: #555; } -/* Style for the cat spayed status */ .cat-spayed { font-size: 14px; margin: 8px 0; - color: #555; /* Text color */ + color: #555; } -/* Style for the cat added timestamp */ .cat-added { font-size: 14px; margin: 8px 0; - color: #555; /* Text color */ + color: #555; } -/* -/* Style for the delete button */ -/* .delete-button { - position: absolute; - top: 10px; - right: 10px; - background-color: red; - color: white; - border: none; - border-radius: 50%; - padding: 5px 10px; - text-align: center; - cursor: pointer; - text-decoration: none; -} */ -/* Style for each cat card */ + .cat-card, .sighting-card, .user-card { - position: relative; /* Add relative positioning to the cat card */ + position: relative; border: 1px solid #ccc; - padding: 16px; - background-color: #fff; + padding: 5px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); border-radius: 5px; } @@ -182,7 +103,7 @@ nav ul li a:hover { top: 10px; right: 10px; background-color: red; - color: white; + color: #ffffff; border: none; border-radius: 50%; padding: 5px 10px; @@ -191,7 +112,6 @@ nav ul li a:hover { text-decoration: none; } -/* Style for the Add Cat Button */ .add-button { position: fixed; bottom: 20px; @@ -206,8 +126,6 @@ nav ul li a:hover { } -/** form**/ -/* Style for form container */ .modal { display: flex; align-items: center; @@ -224,6 +142,7 @@ nav ul li a:hover { } .modal-content { + position:fixed; background-color: #fff; margin: 15% auto; padding: 20px; @@ -245,7 +164,7 @@ nav ul li a:hover { /* Style for form elements */ .form-group { - margin-bottom: 15px; + margin: 10px; } label { @@ -254,16 +173,17 @@ label { .form-control { width: 100%; - padding: 10px; + height: 50px; border: 1px solid #ccc; border-radius: 5px; font-size: 16px; } -select.form-control { - height: 35px; +.form-control select { + height: 50px; } + .file-input { border: none; padding: 0; @@ -286,7 +206,7 @@ select.form-control { } /* Styling for the edit cat modal */ -#edit-cat-modal { +.modal { display: none; position: fixed; z-index: 1; @@ -329,8 +249,7 @@ label { input[type="text"], textarea, select { - width: 100%; - padding: 10px; + padding: 5px; border: 1px solid #ccc; border-radius: 5px; } @@ -477,3 +396,243 @@ button[type="submit"] { margin-right: 5px; } + + +/** home page **/ + + body { + font-family: Arial, sans-serif; + background-color: #f5f5f5; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + min-height: 100vh; + } + + header { + background-color: #333; + color: #fff; + padding: 20px 0; + text-align: center; + } + + h1 { + font-size: 36px; + margin: 0; + } + + .container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; + flex: 1; + } + + .intro { + text-align: center; + margin-bottom: 40px; + } + + .cta-button { + display: inline-block; + padding: 15px 30px; + background-color: #007BFF; + color: #fff; + text-decoration: none; + border-radius: 5px; + font-weight: bold; + } + + .cta-button:hover { + background-color: #0056b3; + } + + .features { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 20px; + } + + .feature { + flex: 1; + background-color: #fff; + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); + border-radius: 5px; + text-align: center; + padding: 20px; + transition: transform 0.2s ease-in-out; + } + + .feature h2 { + font-size: 24px; + margin-bottom: 10px; + } + + .feature p { + font-size: 16px; + color: #555; + } + + .feature:hover { + transform: translateY(-5px); + } + + footer { + background-color: #333; + color: #fff; + text-align: center; + padding: 20px 0; + } + + + .feature a { + text-decoration: none; + color: inherit; + } + + .feature a:hover { + color: #007BFF; + } + + /** Pagination styles **/ +.pagination { + text-align: center; + margin-top: 20px; + margin-bottom: 20px; +} + +.pagination a { + display: inline-block; + padding: 5px 10px; + margin: 2px; + border: 1px solid #ccc; + text-decoration: none; + color: #333; +} + +.pagination .current-page { + font-weight: bold; + background-color: #007BFF; + color: #fff; + padding: 5px 10px; + margin: 2px; + border: 1px solid #007BFF; +} + +.pagination .ellipsis { + margin: 2px; + padding: 5px 10px; + color: #999; +} + +.pagination a.prev-page, +.pagination a.next-page { + background-color: #007BFF; + color: #fff; +} + +.pagination a.prev-page:hover, +.pagination a.next-page:hover { + background-color: #0056b3; +} + +.cat-cards-container, .sighting-cards-container, .user-cards-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(480px, 1fr)); + grid-gap: 16px; + margin: 10px; +} + +/* Cat card styles */ +.cat-card { + border: 1px solid #ccc; + border-radius: 5px; + padding: 10px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); + background-color: #fff; +} + + +.cat-image-container { + margin-bottom: 10px; +} + +.cat-image { + width: 350px; + height: 350px; + object-fit: cover; + border-radius: 10px; +} + +.cat-details { + text-align: left; + flex-grow: 1; + width: 80%; +} + +.cat-title { + font-size: 24px; + margin: 10px 0; + color: #333; +} + +.cat-description { + font-size: 16px; + color: #555; + margin-bottom: 20px; +} + +.cat-metadata { + display: flex; + flex-direction: column; + align-items: flex-start; + margin-bottom: 20px; +} + +.cat-metadata-item { + margin-bottom: 5px; +} + +.cat-metadata-item strong { + font-weight: bold; + margin-right: 5px; +} + +.cat-audio { + width: 100%; + margin-bottom: 10px; +} + +.cat-audio::-webkit-media-controls-panel { + background-color: #f5f5f5; +} + +.cat-audio::-webkit-media-controls-play-button { + color: #007BFF; +} + +.cat-audio::-webkit-media-controls-volume-slider { + width: 80px; +} + +/* Edit button styles */ +.edit-button { + background-color: #007BFF; + color: #fff; + padding: 10px 20px; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.3s ease; +} + +.edit-button:hover { + background-color: #0056b3; +} diff --git a/public/images/logo.jpeg b/public/images/logo.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..b530f66a0702a9a6558805567edcb737f15f8dfe GIT binary patch literal 4363 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 V%8Ah_B7Q{tYk>ZLh>(k!{{Y3u#%urp literal 0 HcmV?d00001 diff --git a/public/images/placeholder.png b/public/images/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc2246b30169e279f74d7006f946878a68afbff GIT binary patch literal 2181 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 literal 0 HcmV?d00001 diff --git a/public/js/login.js b/public/js/login.js index 9c06506..b672c61 100644 --- a/public/js/login.js +++ b/public/js/login.js @@ -1,81 +1,66 @@ -const login = async function (e) { +function isValidEmail(email) { + // Basic email validation using a regular expression + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +} + +function login(e) { e.preventDefault(); + let xhr = new XMLHttpRequest(); + let data = new URLSearchParams(); + data.append("email", document.getElementById("email").value); + data.append("password", document.getElementById("password").value); + + const emailError = document.getElementById("emailError"); + const passwordError = document.getElementById("passwordError"); - try { - const email = document.getElementById("email").value; - const password = document.getElementById("password").value; - const response = await fetch("/login", { - method: "POST", - headers: { - "Accept": "application/x-www-form-urlencoded", - "Content-Type": "application/x-www-form-urlencoded", - }, - credentials: "include", - body: new URLSearchParams({ email, password }), - }); + let isError = false; - if (response.status === 200) { - const result = await response.json(); + if (!isValidEmail(document.getElementById("email").value)) { - if (result.success === true) { - alert("Login success"); - window.location.href = "/"; // Redirect to the home page + emailError.textContent = "Invalid email address"; + emailInput.focus(); + isError = true; + } else { + emailError.textContent = ""; + } + + if (document.getElementById("password").value === "") { + passwordError.textContent = "Password cannot be empty" + passwordError.focus(); + isError = true; + } else { + passwordError.textContent = ""; + } + + if (isError) { + return; + } + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + let result = JSON.parse(xhr.responseText); + console.log(result); + if (result['success'] === true) { + console.log("Success"); + window.location.href = '/'; + } else { + alert("Login failed! Are you sure your credentials are correct?") + } } else { - alert("Login failed"); + alert("Login failed! Are you sure your credentials are correct?") } - } else { - console.log("Request failed"); } - } catch (error) { - console.log(error); - } -}; + }; + xhr.onerror = function () { + console.log("Network error occurred"); + }; -// function login(e){ -// e.preventDefault() -// let xhr = new XMLHttpRequest(); -// let data={ -// "email": document.getElementById("email").value, -// "password": document.getElementById("password").value, -// } -// console.log(data.email); -// -// xhr.onreadystatechange=function (){ -// console.log(xhr.toString()) -// console.log(this.readyState,this.status) -// if (this.readyState==4 && this.status==200){ -// let result=JSON.parse(this.responseText); -// console.log(result.text) -// if (result['status']==true){ -// console.log("success"); -// return; -// } else { -// console.log("fail"); -// return; -// // window.location.href='/'; -// } -// } else { -// console.log("sss"); -// } -// } -// // xhr.onload=function (){ -// // // let res=xhr.responseText -// // // console.log(res); -// // -// // if (xhr.status==200){ -// // let res=xhr.responseText -// // console.log(res); -// // } else { -// // console.error(xhr.status) -// // } -// // } -// -// xhr.open("POST","http://localhost:8000/login/cont",true); -// xhr.setRequestHeader("Accept","application/json"); -// xhr.setRequestHeader("Content-Type","application/json"); -// xhr.withCredentials=true; -// xhr.send(JSON.stringify(data)); -// console.log(xhr.toString()); -// -// } \ No newline at end of file + xhr.open("POST", "/login", true); + xhr.setRequestHeader("Accept", "application/json"); + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + xhr.withCredentials = true; + xhr.send(data.toString()); +} diff --git a/public/js/sighting.js b/public/js/sighting.js index 6a1a930..421efe3 100644 --- a/public/js/sighting.js +++ b/public/js/sighting.js @@ -22,11 +22,13 @@ function closeEditSightingModal() { document.addEventListener("DOMContentLoaded", function () { const addSightingForm = document.getElementById("add-sighting-form"); - console.log("aaa", addSightingForm); - addSightingForm.addEventListener("submit", function (e) { e.preventDefault(); const formData = new FormData(addSightingForm); + let conf = confirm("Are you sure you want to add this sighting?"); + if (!conf) { + return; + } fetch("/sighting", { method: "POST", body: formData, @@ -76,12 +78,11 @@ document.addEventListener("DOMContentLoaded", function () { }) const editSightingForm = document.getElementById("edit-sighting-form"); - console.log("bbb", editSightingForm); editSightingForm.addEventListener("submit", function (event) { + event.preventDefault(); const sightingId = document.getElementById("edit-sighting-id").value; - console.log(sightingId) const formData = new FormData(); formData.append("cat_id", document.getElementById("edit-cat-name-select").value); formData.append("sighting_location", document.getElementById("edit-sighting-location").value); @@ -90,6 +91,10 @@ document.addEventListener("DOMContentLoaded", function () { formData.append("sighting_description", document.getElementById("edit-sighting-description").value); formData.append("image_url", document.getElementById("edit-image_url").files[0]); + let conf = confirm("Are you sure you want to edit this sighting?"); + if (!conf) { + return; + } fetch(`/sighting/${sightingId}`, { method: "POST", body: formData, @@ -120,12 +125,12 @@ document.addEventListener("DOMContentLoaded", function () { const pageNo = searchForm.querySelector("[name='pageNo']").value; const pageSize = searchForm.querySelector("[name='pageSize']").value; - const checkboxes = searchForm.querySelector("[name='order[]']"); + const checkboxes = searchForm.querySelectorAll("[name='order[]']"); + console.log(checkboxes); const selectedOptions = Array.from(checkboxes) .filter(checkbox => checkbox.checked) .map(checkbox => checkbox.value); const order = selectedOptions.join(","); - const queryString = `/sighting?search=${search}&isDesc=${isDesc}&order=${order}&pageNo=${pageNo}&pageSize=${pageSize}`; window.location.href = queryString; @@ -158,7 +163,7 @@ function editSighting(sightingId) { } function populateSightingForm(sightingCard, sightingId) { - const catId = sightingCard.querySelector(".sighting-cat").textContent.trim(); + const catId = sightingCard.querySelector(".sighting-cat-id").textContent.trim(); const sightingLocation = sightingCard.querySelector(".sighting-location").textContent.trim(); const sightingDate = sightingCard.querySelector(".sighting-date").textContent.trim(); const sightingTime = sightingCard.querySelector(".sighting-time").textContent.trim(); diff --git a/public/view/cats.php b/public/view/cats.php index c52f794..fc7b3ab 100644 --- a/public/view/cats.php +++ b/public/view/cats.php @@ -62,43 +62,58 @@ value="<?= isset($_GET['pageSize']) ? intval($_GET['pageSize']) : 10 ?>"> </form> </div> - <div class="cat-cards-container"> <?php foreach ($responseCats as $cat): ?> <div class="cat-card" id="cat-card-<?= $cat['cat_id'] ?>"> <?php if (isset($_SESSION['isAdmin']) && $_SESSION['isAdmin']): ?> - <!-- Edit and delete buttons for admin users --> <a class="delete-button" href="/cat/<?= htmlspecialchars($cat['cat_id']) ?>">X</a> <?php endif; ?> - <!-- <a class="delete-button" href="/cat/<?= htmlspecialchars($cat['cat_id']) ?>">X</a> --> - <h2 class="cat-title"> - <?= htmlspecialchars($cat['name']) ?> - </h2> - <strong>Gender:</strong> - <p class="cat-gender"> - <?= htmlspecialchars($cat['gender']) ?> - </p> - <strong>Color:</strong> - <p class="cat-color"> - <?= htmlspecialchars($cat['color']) ?> - </p> - <strong>Description:</strong> - <p class="cat-description"> - <?= htmlspecialchars($cat['description']) ?> - </p> - <strong>Location:</strong> - <p class="cat-location"> - <?= htmlspecialchars($cat['location']) ?> - </p> - <strong>Spayed:</strong> - <p class="cat-spayed"> - <?= htmlspecialchars($cat['spayed']) ?> - </p> - <strong>Added:</strong> - <p class="cat-added"> - <?= htmlspecialchars($cat['cat_added']) ?> - </p> - <img class="cat-image" src="public/<?= htmlspecialchars($cat['image_path']) ?>" alt="cat image"> + <div class="cat-image-container"> + <img class="cat-image" onerror="this.src='public/images/placeholder.png'" + src="public/<?= htmlspecialchars($cat['image_path']) ?>" alt="cat image"> + </div> + <div class="cat-details"> + <div class="cat-info"> + <h2 class="cat-title"> + <?= htmlspecialchars($cat['name']) ?> + </h2> + <p class="cat-description"> + <?= htmlspecialchars($cat['description']) ?> + </p> + </div> + <div class="cat-metadata"> + <div class="cat-metadata-item"> + <strong>Gender:</strong> + <span class="cat-gender"> + <?= htmlspecialchars($cat['gender']) ?> + </span> + </div> + <div class="cat-metadata-item"> + <strong>Color:</strong> + <span class="cat-color"> + <?= htmlspecialchars($cat['color']) ?> + </span> + </div> + <div class="cat-metadata-item"> + <strong>Location:</strong> + <span class="cat-location"> + <?= htmlspecialchars($cat['location']) ?> + </span> + </div> + <div class="cat-metadata-item"> + <strong>Spayed:</strong> + <span class="cat-spayed"> + <?= htmlspecialchars($cat['spayed']) ?> + </span> + </div> + <div class="cat-metadata-item"> + <strong>Added:</strong> + <span class="cat-added"> + <?= htmlspecialchars($cat['cat_added']) ?> + </span> + </div> + </div> + </div> <audio class="cat-audio" controls> <source src="public/<?= htmlspecialchars($cat['sound_path']) ?>" type="audio/mpeg"> <source src="public/<?= htmlspecialchars($cat['sound_path']) ?>" type="audio/ogg"> @@ -109,11 +124,12 @@ <?php if (isset($_SESSION['isAdmin']) && $_SESSION['isAdmin']): ?> <!-- Edit and delete buttons for admin users --> <button class="edit-button" onclick="editCat(<?= $cat['cat_id'] ?>)">Edit</button> - <?php endif; ?> </div> <?php endforeach; ?> + </div> + <?php require_once(PROJECT_ROOT_PATH . '/public/components/Pagination.php'); ?> <?php if (isset($_SESSION['isAdmin']) && $_SESSION['isAdmin']): ?> <div> @@ -243,6 +259,7 @@ <button id="add-cat-button" class="add-button" onclick="openAddCatModal()">+</button> </div> <?php endif; ?> + <?php require_once(PROJECT_ROOT_PATH . '/public/components/Footer.php'); ?> <script src="public/js/cats.js"></script> </body> diff --git a/public/view/home.php b/public/view/home.php index 214a530..5a079e9 100644 --- a/public/view/home.php +++ b/public/view/home.php @@ -5,133 +5,44 @@ <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="stylesheet" href="public/css/styles.css"> - <style> - /* Add your CSS styles here */ - body { - font-family: Arial, sans-serif; - background-color: #f5f5f5; - margin: 0; - padding: 0; - } - - header { - background-color: #333; - color: #fff; - padding: 20px 0; - text-align: center; - } - - h1 { - font-size: 36px; - margin: 0; - } - - .container { - max-width: 1200px; - margin: 0 auto; - padding: 20px; - } - - .intro { - text-align: center; - margin-bottom: 40px; - } - - .cta-button { - display: inline-block; - padding: 15px 30px; - background-color: #007BFF; - color: #fff; - text-decoration: none; - border-radius: 5px; - font-weight: bold; - } - - .cta-button:hover { - background-color: #0056b3; - } - - .features { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 20px; - } - - .feature { - flex: 1; - background-color: #fff; - box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); - border-radius: 5px; - text-align: center; - padding: 20px; - transition: transform 0.2s ease-in-out; - } - - .feature h2 { - font-size: 24px; - margin-bottom: 10px; - } - - .feature p { - font-size: 16px; - color: #555; - } - - .feature:hover { - transform: translateY(-5px); - } - - footer { - background-color: #333; - color: #fff; - text-align: center; - padding: 20px 0; - } - </style> - <title>Wild Cats Guide</title> + <link rel="stylesheet" type="text/css" href="public/css/styles.css"> +</head> +<title>Kuchenk Guide</title> </head> <body> <?php require_once(PROJECT_ROOT_PATH . '/public/components/Navbar.php'); ?> <header> - <h1>Welcome to the Wild Cats Guide</h1> - <p>Explore the fascinating world of wild cats at your university</p> - <a href="#features" class="cta-button">Learn More</a> + <h1>Welcome to Kuchenk ITB Guide</h1> + <p>Explore the fascinating world of wild cats at Institut Teknologi Bandung!</p> </header> <div class="container"> <div class="intro"> - <h2>Discover Wild Cats on Campus</h2> - <p>Get to know the wild cats that roam your university campus. Explore their preferred locations, view sightings, - and share your own.</p> + <h2>Discover Kuchenk Gemuk on Campus</h2> + <p>Mari berkenalan dengan kuchenk kuchenk gemuk yang menghuni ITB! Lihatlah informasi tenatang mereka, tempat + mereka biasa nongkrong, dan share foto kalian dengan mereka jika bertemu!</p> </div> <section id="features" class="features"> <div class="feature"> - <h2>Wild Cat List</h2> - <p>View a list of wild cats and their preferred locations on campus.</p> + <a href="/cat"> + <h2>Direktori Kuchenk</h2> + <p>Ayo kenalan dengan kuchenk gemoy ITB dan lokasi tempat mereka biasa nongkrong!</p> + </a> </div> <div class="feature"> - <h2>Sightings</h2> - <p>Share your wild cat sightings with photos and descriptions.</p> + <a href="/sighting"> + <h2>Sightings</h2> + <p>Mari lihat foto orang-orang yang ketemu kuchenk ITB!</p> + </a> </div> - <div class="feature"> - <h2>Add a Sighting</h2> - <p>Contribute to our guide by adding your own wild cat sightings.</p> - </div> </section> </div> - - <footer> - <p>© - <?= date('Y') ?> Wild Cats Guide - </p> - </footer> + <?php require_once(PROJECT_ROOT_PATH . '/public/components/Footer.php'); ?> </body> </html> \ No newline at end of file diff --git a/public/view/login.php b/public/view/login.php index 1df55c0..3386ee5 100644 --- a/public/view/login.php +++ b/public/view/login.php @@ -1,22 +1,30 @@ <!DOCTYPE html> <html lang="en"> - <head> - <meta charset="UTF-8" /> - <title>Title</title> - <script src="./public/js/login.js"></script> - <link rel="stylesheet" href="public/css/styles.css"> - </head> - <body> - <?php require_once(PROJECT_ROOT_PATH.'/public/components/Navbar.php'); ?> - <h1 id="loginTitle">Login</h1> - <form id="inputForm"> - <label for="email" id="emailLabel">email: </label> - <input type="text" id="email" required /> <br /><br /> - <label for="password" id="passwordLabel">password: </label> - <input type="password" id="password" required /> <br /><br /> - <button type="submit" id="loginButton" onclick="login(event)"> - Login - </button> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Login</title> + <script src="public/js/login.js"></script> + <link rel="stylesheet" type="text/css" href="public/css/login.css"> +</head> + +<body> + <div class="login-container"> + <div class="login-logo"> + <img src="public/images/logo.jpeg" alt="Your Logo"> + </div> + <h1 class="login-title">Welcome Back</h1> + <form class="login-form" id="inputForm"> + <label for="email" class="login-label">Email:</label> + <input type="text" id="email" class="login-input" required> + <span id="emailError" class="error-message"></span><br><br> + <label for="password" class="login-label">Password:</label> + <input type="password" id="password" class="login-input" required><br><br> + <span id="passwordError" class="error-message"></span><br><br> + <button type="submit" class="login-button" onclick="login(event)">Login</button> </form> - </body> -</html> + </div> +</body> + +</html> \ No newline at end of file diff --git a/public/view/sighting.php b/public/view/sighting.php index b24a572..1a0517d 100644 --- a/public/view/sighting.php +++ b/public/view/sighting.php @@ -31,56 +31,62 @@ </div> <input type="hidden" name="pageNo" value="<?= isset($_GET['pageNo']) ? intval($_GET['pageNo']) : 1 ?>"> <input type="hidden" name="pageSize" - value="<?= isset($_GET['pageSize']) ? intval($_GET['pageSize']) : 10 ?>"> + value="<?= isset($_GET['pageSize']) ? intval($_GET['pageSize']) : 10 ?>"> </form> </div> - <div class="sighting-cards-container"> - <?php foreach ($responseSightings as $sighting):?> - <div class="sighting-card" id="sighting-card-<?=$sighting['sighting_id']?>"> - <?php if (isset($_SESSION['user_id']) &&$_SESSION['user_id']==$sighting['user_id']){ - echo ("<a class=\"delete-button\" href='sighting/" . $sighting['sighting_id'] . "'>X<a>"); - }?> -<!-- --><?php //if ($_SESSION['user_id']===$sighting['user_id']): ?> -<!-- <a class="delete-button" href="/sighting/--><?php //= htmlspecialchars($sighting['sighting_id'])?><!--">X</a>--> -<!-- --><?php //endif;?> - <strong>Kucing:</strong> - <p class="sighting-cat"> - <?=htmlspecialchars($sighting['name'])?> - </p> - <strong>Sighted by:</strong> - <p class="sighting-username"> - <?=htmlspecialchars($sighting['username'])?> - </p> - <strong>Location:</strong> - <p class="sighting-location"> - <?=htmlspecialchars($sighting['sighting_location'])?> - </p> - <strong>Date:</strong> - <p class="sighting-date"> - <?=htmlspecialchars($sighting['date'])?> - </p> - <strong>Time:</strong> - <p class="sighting-time"> - <?=htmlspecialchars($sighting['time'])?> - </p> - <br> - <p class="sighting-description"> - <?=htmlspecialchars($sighting['sighting_description'])?> - </p> - <img class="cat-image" src="public/<?= htmlspecialchars($sighting['image_url'])?>" alt="cat_image"> - <?php if (isset($_SESSION['user_id'])&&$_SESSION['user_id']===$sighting['user_id']): ?> - <a class="edit-button" onclick="editSighting(<?= $sighting['sighting_id']?>)">Edit</a> - <?php endif;?> + <div class="cat-cards-container"> + <?php foreach ($responseSightings as $sighting): ?> + <div class="cat-card" id="sighting-card-<?= $sighting['sighting_id'] ?>"> + <?php if (isset($_SESSION['user_id']) && $_SESSION['user_id'] == $sighting['user_id']) { + echo ("<a class=\"delete-button\" href='sighting/" . $sighting['sighting_id'] . "'>X</a>"); + } ?> + <div class="cat-image-container"> + <img class="cat-image" onerror="this.src='public/images/placeholder.png'" + src="public/<?= htmlspecialchars($sighting['image_url']) ?>" alt="sighting image"> + </div> + <div class="cat-details"> + <div class="cat-info"> + <h2 class="cat-title"> + <?= htmlspecialchars($sighting['name']) ?> + </h2> + <p class="sighting-description"> + <?= htmlspecialchars($sighting['sighting_description']) ?> + </p> + </div> + <div class="cat-metadata"> + <div class="cat-metadata-item"> + <strong>Sighted by:</strong> + <span class="sighting-username"> + <?= htmlspecialchars($sighting['username']) ?> + </span> + </div> + <div class="cat-metadata-item"> + <strong>Location:</strong> + <span class="sighting-location"> + <?= htmlspecialchars($sighting['sighting_location']) ?> + </span> + </div> + <div class="cat-metadata-item"> + <strong>Date:</strong> + <span class="sighting-date"> + <?= htmlspecialchars($sighting['date']) ?> + </span> + </div> + <div class="cat-metadata-item"> + <strong>Time:</strong> + <span class="sighting-time"> + <?= htmlspecialchars($sighting['time']) ?> + </span> + </div> + </div> + </div> + <?php if (isset($_SESSION['user_id']) && $_SESSION['user_id'] === $sighting['user_id']): ?> + <button class="edit-button" onclick="editSighting(<?= $sighting['sighting_id'] ?>)">Edit</button> + <?php endif; ?> </div> - <!-- <p>--> - <!-- --><?php //= htmlspecialchars($sighting['nama'])?> - <!-- </p>--> - <!-- <img src="public/--><?php //=htmlspecialchars($sighting['image_url'])?><!--">--> - <?php endforeach;?> + <?php endforeach; ?> </div> -<!-- foreach ($responseCats as $cat){--> -<!-- echo ('<option value="' . $cat['cat_id'] . '">' . $cat['name'] . '</option>');--> -<!-- }?>--> + <?php require_once(PROJECT_ROOT_PATH . '/public/components/Pagination.php'); ?> <?php if (isset($_SESSION['user_id'])): ?> <div> @@ -93,9 +99,9 @@ <div class="form-group"> <label for="edit-cat-name-select">Cat name:</label> <select id="edit-cat-name-select" name="cat_id" class="form-control"> - <?php foreach ($responseCats as $cat){ + <?php foreach ($responseCats as $cat) { echo ('<option value="' . $cat['cat_id'] . '">' . $cat['name'] . '</option>'); - }?> + } ?> </select> </div> <div class="form-group"> @@ -112,12 +118,13 @@ </div> <div class="form-group"> <label for="edit-sighting-description">Description:</label> - <textarea id="edit-sighting-description" name="sighting_description" class="form-control"></textarea> + <textarea id="edit-sighting-description" name="sighting_description" + class="form-control"></textarea> </div> <div class="form-group"> <label for="edit-image_url">Image File (JPG, JPEG, PNG):</label> <input type="file" id="edit-image_url" name="image_url" class="file-input" - accept=".jpg, .jpeg, .png"> + accept=".jpg, .jpeg, .png"> </div> <button type="submit" class="submit-button">Edit Sighting</button> </form> @@ -125,8 +132,9 @@ </div> </div> <div> -<!-- --><?php //echo ("<p>aaaA</p>")?> - <div id="add-sighting-modal" class="modal" > + <!-- --> + <?php //echo ("<p>aaaA</p>")?> + <div id="add-sighting-modal" class="modal"> <div class="modal-content"> <span class="close" onclick="closeAddSightingModal()">×</span> <h2>Add New Sighting</h2> @@ -135,9 +143,9 @@ <div class="form-group"> <label for="cat-name">Cat name:</label> <select id="cat-name-select" name="cat_id" class="form-control"> - <?php foreach ($responseCats as $cat){ + <?php foreach ($responseCats as $cat) { echo ('<option value="' . $cat['cat_id'] . '">' . $cat['name'] . '</option>'); - }?> + } ?> </select> </div> <div class="form-group"> @@ -167,7 +175,9 @@ </div> <button id="add-sighting-button" class="add-button" onclick="openAddSightingModal()">+</button> </div> - <?php endif;?> + <?php endif; ?> + <?php require_once(PROJECT_ROOT_PATH . '/public/components/Footer.php'); ?> <script src="public/js/sighting.js"></script> </body> + </html> \ No newline at end of file diff --git a/public/view/users.php b/public/view/users.php index a5e99dc..05fb10a 100644 --- a/public/view/users.php +++ b/public/view/users.php @@ -11,8 +11,8 @@ <?php require_once(PROJECT_ROOT_PATH . '/public/components/Navbar.php'); ?> <div class="user-cards-container"> <?php foreach ($responseUsers as $user): ?> - <div class="user-card" id="user-card-<?= $user['user_id'] ?>"> - <a class="delete-button" href="/user/<?= htmlspecialchars($user['user_id']) ?>">Delete</a> + <div class="cat-card" id="user-card-<?= $user['user_id'] ?>"> + <a class="delete-button" href="/user/<?= htmlspecialchars($user['user_id']) ?>">X</a> <h2 class="user-title"> <?= htmlspecialchars($user['nama']) ?> </h2> @@ -68,6 +68,7 @@ </div> </div> </div> + <?php require_once(PROJECT_ROOT_PATH . '/public/components/footer.php'); ?> <script src="public/js/users.js"></script> </body> diff --git a/src/bases/BaseRepository.php b/src/bases/BaseRepository.php index d5b44aa..18ce80e 100644 --- a/src/bases/BaseRepository.php +++ b/src/bases/BaseRepository.php @@ -86,8 +86,8 @@ abstract class BaseRepository $offset = ($page - 1) * $perPage; $query .= " LIMIT $perPage OFFSET $offset"; } - - echo $query; + // UNCOMMENT THIS FOR DEBUG + // echo $query; $stmt = $this->pdo->prepare($query); $stmt->execute(); diff --git a/src/repositories/SightingRepository.php b/src/repositories/SightingRepository.php index e74a6e5..e3510e4 100644 --- a/src/repositories/SightingRepository.php +++ b/src/repositories/SightingRepository.php @@ -21,31 +21,41 @@ class SightingRepository extends BaseRepository return self::$instance; } - public function createSighting($sightingData){ -// foreach ($sightingData as $key => $value){ + public function createSighting($sightingData) + { + // foreach ($sightingData as $key => $value){ // echo ($key.":" .$value."<br>"); // } - return $this->insert($sightingData,array( - 'cat_id' => PDO::PARAM_INT, - 'user_id' => PDO::PARAM_INT, - 'sighting_location' => PDO::PARAM_STR, - 'date' => PDO::PARAM_STR, - 'time' => PDO::PARAM_STR, - 'sighting_description' => PDO::PARAM_STR, - 'image_url' => PDO::PARAM_STR - )); + return $this->insert( + $sightingData, + array( + 'cat_id' => PDO::PARAM_INT, + 'user_id' => PDO::PARAM_INT, + 'sighting_location' => PDO::PARAM_STR, + 'date' => PDO::PARAM_STR, + 'time' => PDO::PARAM_STR, + 'sighting_description' => PDO::PARAM_STR, + 'image_url' => PDO::PARAM_STR + ) + ); } - public function updateSighting($sightingId, $sightingData){ - return $this->update("sighting_id",$sightingId,$sightingData, array( - 'cat_id' => PDO::PARAM_INT, - 'user_id' => PDO::PARAM_INT, - 'sighting_location' => PDO::PARAM_STR, - 'date' => PDO::PARAM_STR, - 'time' => PDO::PARAM_STR, - 'sighting_description' => PDO::PARAM_STR, - 'image_url' => PDO::PARAM_STR - )); + public function updateSighting($sightingId, $sightingData) + { + return $this->update( + "sighting_id", + $sightingId, + $sightingData, + array( + 'cat_id' => PDO::PARAM_INT, + 'user_id' => PDO::PARAM_INT, + 'sighting_location' => PDO::PARAM_STR, + 'date' => PDO::PARAM_STR, + 'time' => PDO::PARAM_STR, + 'sighting_description' => PDO::PARAM_STR, + 'image_url' => PDO::PARAM_STR + ) + ); } public function deleteSighting($sighting) @@ -53,47 +63,38 @@ class SightingRepository extends BaseRepository return $this->delete("sighting_id", $sighting); } -// public function join(){ + // public function join(){ // $sql = "SELECT * FROM sightings s join (cats c, users u) on (c.cat_id=s.cat_id and u.user_id=s.user_id)"; // $stmt = $this->pdo->prepare($sql); // $stmt->execute(); // return $stmt->fetchAll(PDO::FETCH_ASSOC); // } - public function join($columns = "*",$where=[],$orderBy=[],$page=1,$perPage=10,$isDesc=false){ + public function join($columns = "*", $where = [], $orderBy = [], $page = 1, $perPage = 10, $isDesc = false) + { $query = "SELECT $columns FROM sightings s join (cats c, users u) on (c.cat_id=s.cat_id and u.user_id=s.user_id)"; -// echo "aaaa"; -// echo "<br>"; -// echo $query; -// $query = "select * from cats c join sightings s on c.cat_id=s.cat_id join users u on s.user_id=u.user_id"; - if (!empty($where)){ - $query .= " WHERE ".implode(" AND ",$where); -// echo "<br>"; -// echo $query; + + if (!empty($where)) { + $query .= " WHERE " . implode(" AND ", $where); } -// echo ("<br>"); -// echo $query; -// echo ("<br>"); - if (!empty($orderBy)){ - $orderByWithDesc = array_map(function ($column) use ($isDesc){ - return $column .($isDesc && $isDesc!="false"?" DESC": ""); - },$orderBy); - $query .= " ORDER BY ".implode(", ", $orderByWithDesc); + if (!empty($orderBy)) { + $orderByWithDesc = array_map(function ($column) use ($isDesc) { + return $column . ($isDesc && $isDesc != "false" ? " DESC" : ""); + }, $orderBy); + $query .= " ORDER BY " . implode(", ", $orderByWithDesc); } if (!empty($perPage)) { $offset = ($page - 1) * $perPage; $query .= " LIMIT $perPage OFFSET $offset"; } - echo $query; - echo ("<br>"); $stmt = $this->pdo->prepare($query); $stmt->execute(); $result = $stmt->fetchAll(PDO::FETCH_ASSOC); $countQuery = "SELECT COUNT(*) as total FROM sightings s join (cats c, users u) on (c.cat_id=s.cat_id and u.user_id=s.user_id)"; - if (!empty($where)){ - $countQuery .= " WHERE ".implode(" AND ", $where); + if (!empty($where)) { + $countQuery .= " WHERE " . implode(" AND ", $where); } $stmtCount = $this->pdo->prepare($countQuery); $stmtCount->execute(); @@ -106,7 +107,8 @@ class SightingRepository extends BaseRepository } - public function getSightingById($sightingId){ - return $this->selectOne("*",["sighting_id = $sightingId"]); + public function getSightingById($sightingId) + { + return $this->selectOne("*", ["sighting_id = $sightingId"]); } } \ No newline at end of file -- GitLab