diff --git a/src/Controllers/AuthController.php b/src/Controllers/AuthController.php index 235a3278ebea824a651bcb1f0fa51dd8a397fa7c..97a5bb1b70434ba36e657d98dfe120dfdf5d7aff 100644 --- a/src/Controllers/AuthController.php +++ b/src/Controllers/AuthController.php @@ -17,10 +17,6 @@ use app\Utils\Toast; class AuthController extends Controller { - public function index() - { - $this->render('index'); - } public function login() { diff --git a/src/Controllers/DormController.php b/src/Controllers/DormController.php index 6c1f169b5d428e4d89fbd7df6aa773c53762d473..d4509214903e73f80e08192971b424bfbc60b255 100644 --- a/src/Controllers/DormController.php +++ b/src/Controllers/DormController.php @@ -15,6 +15,16 @@ use app\Utils\FileManager; class DormController extends Controller { + + public function list() + { + $page = Request::getPage(); + $data = Dorm::getAllFiltered($page); + $data["page"] = $page; + $this->render('index', $data); + } + + public function create() { $form = new Validation([ diff --git a/src/Core/Application.php b/src/Core/Application.php index 563a07596c957af78497880f89d73a4418704a7e..c24a4eb9b6af3a912071003097e62a8ef5ca53df 100644 --- a/src/Core/Application.php +++ b/src/Core/Application.php @@ -32,7 +32,7 @@ class Application public function configureRoutes() { $this->router = new Router(); - $this->router->get('/', [], AuthController::class, 'index'); + $this->router->get('/', [], DormController::class, 'list'); $this->router->methods(["GET", "POST"], '/login', [], AuthController::class, 'login'); $this->router->methods(["GET", "POST"], '/register', [], AuthController::class, 'register'); $this->router->get('/logout', [], AuthController::class, 'logout'); @@ -46,4 +46,4 @@ class Application $this->router->methods(["GET", "POST"], "/dorms/create", [AdminOnly::class], DormController::class, 'create'); $this->router->methods(["GET", "POST"], "/dorms/{dormId}/media", [AdminOnly::class], DormController::class, 'media'); } -} \ No newline at end of file +} diff --git a/src/Core/Request.php b/src/Core/Request.php index d5798187ea5a3679f5a994716ccb618bae9d35f7..35f1662d73315205039c7c11b10cdca6302c019c 100644 --- a/src/Core/Request.php +++ b/src/Core/Request.php @@ -59,4 +59,14 @@ class Request { return $_FILES[$fieldName] ?? null; } + + public static function getPage() + { + $page = $_GET['page'] ?? "1"; + try { + return intval($page); + } catch (\Throwable $th) { + return 1; + } + } } diff --git a/src/Models/BaseModel.php b/src/Models/BaseModel.php index 0e341c7ca9e1bb0805f7725b901a2e64388c62a0..886477f946c65360b0596b8e81194fb2df303ccb 100644 --- a/src/Models/BaseModel.php +++ b/src/Models/BaseModel.php @@ -4,17 +4,18 @@ namespace app\Models; use app\Core\Application; use app\Core\Database; +use PDO; #[\AllowDynamicProperties] class BaseModel { - protected static Database $db; + protected static PDO $db; protected static string $table = ""; protected static string $primaryKey = ""; public static function connect() { - self::$db = Application::$db; + self::$db = Application::$db->pdo; } public static function toModel($data): self @@ -28,7 +29,7 @@ class BaseModel return $model; } - protected static function toModelArray($data) + public static function toModelArray($data) { $models = []; foreach ($data as $row) { @@ -39,14 +40,14 @@ class BaseModel public static function all($orderBy = null) { - $stmt = self::$db->pdo->prepare("SELECT * FROM " . static::$table . ($orderBy ? " ORDER BY " . $orderBy : "")); + $stmt = self::$db->prepare("SELECT * FROM " . static::$table . ($orderBy ? " ORDER BY " . $orderBy : "")); $stmt->execute(); return self::toModelArray($stmt->fetchAll()); } public static function findById(int $id): static { - $stmt = self::$db->pdo->prepare("SELECT * FROM " . static::$table . " WHERE " . static::$primaryKey . " = :id"); + $stmt = self::$db->prepare("SELECT * FROM " . static::$table . " WHERE " . static::$primaryKey . " = :id"); $stmt->bindValue(":id", $id); $stmt->execute(); $data = $stmt->fetch(); @@ -58,7 +59,7 @@ class BaseModel public static function deleteById(int $id) { - $stmt = self::$db->pdo->prepare("DELETE FROM " . static::$table . " WHERE " . static::$primaryKey . " = :id"); + $stmt = self::$db->prepare("DELETE FROM " . static::$table . " WHERE " . static::$primaryKey . " = :id"); $stmt->bindValue(":id", $id); $stmt->execute(); return $stmt->rowCount(); @@ -73,7 +74,7 @@ class BaseModel } $where = substr($where, 0, -5); - $stmt = self::$db->pdo->prepare("SELECT * FROM " . static::$table . " WHERE " . $where); + $stmt = self::$db->prepare("SELECT * FROM " . static::$table . " WHERE " . $where); foreach ($arrFilter as $key => $value) { $stmt->bindValue(":" . $key, $value); } @@ -90,7 +91,7 @@ class BaseModel } $where = substr($where, 0, -5); - $stmt = self::$db->pdo->prepare("SELECT * FROM " . static::$table . " WHERE " . $where . " LIMIT 1"); + $stmt = self::$db->prepare("SELECT * FROM " . static::$table . " WHERE " . $where . " LIMIT 1"); foreach ($arrFilter as $key => $value) { $stmt->bindValue(":" . $key, $value); } @@ -122,7 +123,7 @@ class BaseModel $sql = "INSERT INTO " . static::$table . " (" . implode(",", $keys) . ") VALUES (:" . implode(",:", $keys) . ") RETURNING " . static::$primaryKey . ";"; - $stmt = self::$db->pdo->prepare($sql); + $stmt = self::$db->prepare($sql); foreach ($fields as $key => $value) { $stmt->bindValue(":" . $key, $value); @@ -149,7 +150,7 @@ class BaseModel $sql = substr($sql, 0, -2); $sql .= " WHERE " . static::$primaryKey . " = :" . static::$primaryKey . ";"; - $stmt = self::$db->pdo->prepare($sql); + $stmt = self::$db->prepare($sql); foreach ($fields as $key => $value) { $stmt->bindValue(":" . $key, $value); @@ -163,7 +164,7 @@ class BaseModel { $sql = "DELETE FROM " . static::$table . " WHERE " . static::$primaryKey . " = :" . static::$primaryKey . ";"; - $stmt = self::$db->pdo->prepare($sql); + $stmt = self::$db->prepare($sql); $stmt->bindValue(":" . static::$primaryKey, $this->{static::$primaryKey}); diff --git a/src/Models/Dorm.php b/src/Models/Dorm.php index 3157abf1aa9222d3c1e90e7da8f82ac47e5e9a81..89edd8d809951c689964b5a566ff10cc58f62a44 100644 --- a/src/Models/Dorm.php +++ b/src/Models/Dorm.php @@ -14,4 +14,57 @@ class Dorm extends BaseModel public int $owner_id; protected static string $table = "dorms"; protected static string $primaryKey = "dorm_id"; + + public static function getAllFiltered($page = 1, $orderBy = "dorm_id DESC", $search = "") + { + $limit = 4; + $offset = ($page - 1) * $limit; + + $stmt = self::$db->prepare( + $search ? + "SELECT * FROM dorms WHERE name ILIKE :1 ORDER BY :2 LIMIT :3 OFFSET :4" : + "SELECT * FROM dorms ORDER BY :2 LIMIT :3 OFFSET :4" + ); + + if ($search) { + $stmt->bindValue(":1", "%{$search}%"); + } + $stmt->bindValue(":2", $orderBy); + $stmt->bindValue(":3", $limit); + $stmt->bindValue(":4", $offset); + + $stmt->execute(); + $dorms = self::toModelArray($stmt->fetchAll()); + + $stmt = self::$db->prepare( + $search ? + "SELECT COUNT(dorm_id) FROM dorms WHERE name ILIKE :1" : + "SELECT COUNT(dorm_id) FROM dorms" + ); + + if ($search) { + $stmt->bindValue(":1", "%{$search}%"); + } + + $stmt->execute(); + + $count = $stmt->fetchColumn(); + $totalPage = ceil($count / $limit); + + $dormIds = array_map(fn ($dorm) => $dorm->dorm_id, $dorms); + + $stmt = self::$db->prepare("SELECT * FROM medias + NATURAL JOIN ( + SELECT MIN(media_id) AS media_id, dorm_id FROM medias WHERE type = 'photo' AND dorm_id IN (" . implode(",", $dormIds) . ") GROUP BY dorm_id + ) AS m"); + $stmt->execute(); + + $medias = Media::toModelArray($stmt->fetchAll()); + $medias = array_reduce($medias, function ($acc, $media) { + $acc[$media->dorm_id] = $media; + return $acc; + }, []); + + return ["dorms" => $dorms, "medias" => $medias, "totalPage" => $totalPage]; + } } diff --git a/src/Views/index.php b/src/Views/index.php index 4cf6a4336eb30b64950777f780252099c702176e..077aa691e3124c3f69a4c8a4c3e35284a71cba3d 100644 --- a/src/Views/index.php +++ b/src/Views/index.php @@ -7,8 +7,29 @@ <p class="hero-desc">Dapatkan infonya dan langsung sewa di MyKos. </p> <? if ($user->is_admin ?? false) : ?> - <div class="cta-admin"> - <a href="/dorms/create" class="btn btn-primary">Tambah Kos</a> - </div> + <div class="cta-admin"> + <a href="/dorms/create" class="btn btn-primary">Tambah Kos</a> + </div> <? endif; ?> + <ul class="dorm_list"> + <? foreach ($dorms as $dorm) : ?> + <li class="dorm_item"> + <a href="/dorms/<?= $dorm->dorm_id ?>"> + <? $media = array_key_exists($dorm->dorm_id, $medias) ? $medias[$dorm->dorm_id] : null ?> + <? if ($media) : ?> + <img class="dorm_image" src="<?= $media->endpoint ?>" alt="<?= $media->alt_text ?>" /> + <? else : ?> + <div class="dorm_image"></div> + <? endif; ?> + <div class="dorm_item__content"> + <h3 class="dorm_item__title line-clamp-1"><?= $dorm->name ?></h3> + <p class="dorm_item__city"><?= $dorm->city ?></p> + <p class="dorm_item__price">Rp<?= number_format($dorm->price, 0, ',', '.') ?> + <span> / bulan</span> + </p> + </div> + </a> + </li> + <? endforeach; ?> + </ul> </section> \ No newline at end of file diff --git a/src/public/static/styles/home.css b/src/public/static/styles/home.css index 6d0bf26b1d81e48af1e592d7469b84691b664e1c..d9305c5fb5dce3009ba152d1a6e13bd61fc5a6f8 100644 --- a/src/public/static/styles/home.css +++ b/src/public/static/styles/home.css @@ -21,3 +21,52 @@ min-width: 10rem; text-align: center; } + +.dorm_list { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 2rem; + margin-top: 4rem; +} + +.dorm_item__title { + color: #303030; + font-size: var(--text-sm); + margin-top: 0.5rem; +} + +.dorm_item__city { + color: #404040; + font-size: var(--text-sm); + font-weight: 700; + margin-top: 0.5rem; +} + +.dorm_item__price { + color: #303030; + font-size: var(--text-base); + font-weight: 700; + margin-top: 0.5rem; +} + +.dorm_item__price span { + font-weight: 400; +} + +.dorm_image { + width: 100%; + aspect-ratio: 4/3; + object-fit: cover; + background-color: white; + border-radius: 1rem; + border: 1px solid var(--color-gray-darker); + transition: all 0.1s ease-in-out; +} + +.dorm_item:hover .dorm_image { + transform: scale(1.05); +} + +div.dorm_image { + background-color: var(--color-gray-darker); +} diff --git a/src/public/static/styles/main.css b/src/public/static/styles/main.css index 7995ecda11e0819e0dd46e95088345a14ff07699..1d5b2333e4e031f0482f0a7ef8b114bfbdbb9aad 100644 --- a/src/public/static/styles/main.css +++ b/src/public/static/styles/main.css @@ -17,6 +17,13 @@ html { --text-sm: 0.875rem; } +.line-clamp-1 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} + body { min-height: 100%; display: flex; @@ -385,7 +392,7 @@ td { .slides-container { max-width: 100%; width: 100%; - aspect-ratio: 16 / 9; + aspect-ratio: 4 / 3; display: flex; list-style: none; margin: 0; @@ -403,7 +410,7 @@ td { video { width: 100%; height: 100%; - object-fit: contain; + object-fit: cover; } .slides-container {