diff --git a/public/components/Footer.php b/public/components/Footer.php new file mode 100644 index 0000000000000000000000000000000000000000..7be83cca43d877d31387a7237c22c4c787954683 --- /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 c2c26e9ff5117b8a5b045fffd2e161079f5cf957..3e3f0aad05f8e1f501b8fee32d156e3bee76796f 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 12faf5e63131dba5aac926329d0fa2ef4f724f09..958ac59f3fd7d973b4c3ff733790b7efd0f8aa6d 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 0000000000000000000000000000000000000000..6bf0a0c65c75ca226ae9004123b0437f787c2bd0 --- /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 f39b28da09a37e318209b6f9358104f5d46195ff..f090cf50d1e1c303b987f319dc752407e8e6fc71 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 Binary files /dev/null and b/public/images/logo.jpeg differ diff --git a/public/images/placeholder.png b/public/images/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc2246b30169e279f74d7006f946878a68afbff Binary files /dev/null and b/public/images/placeholder.png differ diff --git a/public/js/login.js b/public/js/login.js index 9c06506beb4ed14a206c6a934697abccc94ce648..b672c619d7f2e36dce15852e69f16b3d59aa53db 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 6a1a930f6abab71ed3d7e05066aeb4adb8c3c22c..421efe37a76f6afc81063a485aaafadf73cf1112 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 c52f794736c9fa36b405e1a298650c9a1ccc192e..fc7b3ab87a2a92a1b96fc436269fa3e23b613fdb 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 214a530a106aa04dc004167e1b004e83cd7af6c0..5a079e980f62889e0e8eb63e8c14c18321f79bb1 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 1df55c03bb6cf9d952d6dbe81c7668e5cbe77406..3386ee54e29681c7bf114cf7204fbafa1e46eb2a 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 b24a57289b3faa11fe9518f25a3407f8349c96ce..1a0517d922aafdf29520d556b1ae72450441cd46 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 a5e99dc91bbf6b281441d724e81f2783dc96a53d..05fb10a051bcbe41cf65a3b80868bffa86280a2b 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 d5b44aac93cb44e2da4ac19f05c57944ff96f956..18ce80ee0fc41a400fc1c53c4bc7d22276552c64 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 e74a6e528d93160196f80507198425ff73d7ebba..e3510e4f5b8042e6cab7d5ca461b04278265fae3 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