diff --git a/src/Controllers/DormController.php b/src/Controllers/DormController.php
index 983ab8621636fdf04808fb4c4b0481c74ab185b6..0d56dcbb5847ae239b61f84f31dfce145afe89bf 100644
--- a/src/Controllers/DormController.php
+++ b/src/Controllers/DormController.php
@@ -11,6 +11,7 @@ use app\Forms\Validation;
 use app\Models\Dorm;
 use app\Models\Media;
 use app\Models\Owners;
+use app\Models\Reviews;
 use app\Utils\FileManager;
 
 class DormController extends Controller
@@ -27,6 +28,27 @@ class DormController extends Controller
     }
     $this->render('index', $data);
   }
+  
+  public function view($params)
+  {
+    $dormId = $params["dormId"];
+    $dorm = Dorm::findById($dormId);
+    $medias = Media::findByDormId($dormId);
+    $owner = Owners::findById($dorm->owner_id);
+    $reviews = Reviews::findByDormId($dorm->dorm_id);
+
+    if (Request::getMethod() === "POST") {
+      Response::redirect("/dorms/{$dorm->dorm_id}/edit");
+      return;
+    } 
+
+    $this->render("dorm/view", [
+      "dorm" => $dorm,
+      "medias" => $medias,
+      "owner" => $owner,
+      "reviews" => $reviews
+    ]);
+  }
 
 
   public function create()
diff --git a/src/Controllers/ReviewController.php b/src/Controllers/ReviewController.php
new file mode 100644
index 0000000000000000000000000000000000000000..dde38bbe933b399f356a9bf00f8b0eeff7023bff
--- /dev/null
+++ b/src/Controllers/ReviewController.php
@@ -0,0 +1,127 @@
+<?php
+
+
+namespace app\Controllers;
+
+use app\Core\Request;
+use app\Core\Response;
+use app\Forms\Field;
+use app\Forms\Phone;
+use app\Forms\Email;
+use app\Forms\Required;
+use app\Forms\Validation;
+use app\Forms\Minimum;
+use app\Forms\Number;
+use app\Forms\Rating;
+use app\Models\Owners;
+use app\Models\Reviews;
+use app\Models\User;
+use app\Utils\Toast;
+
+
+class ReviewController extends Controller
+{
+  public function __construct()
+  {
+    Response::$title = "Review | MyKos";
+  }
+
+  public function getAllByDormId($params)
+  {
+    $dorm = $params['dormId'];
+    $reviews = Reviews::findByDormId($dorm->dorm_id);
+    $this->render("reviews/edit", [
+        "dorm_id" => $dorm->dorm_id,
+        "reviews" => $reviews
+      ]);
+  }
+
+  public function create($params)
+  {
+    $form = $this->getReviewForm();
+    $dormId = $params['dormId'];
+
+
+    if (Request::getMethod() === "POST") {
+      if ($form->validate(Request::getBody())) {
+        $review = Reviews::toModel($form->data);
+        $review->user_id = Request::getUser()->user_id;
+        $review->dorm_id = $dormId;
+        $review->save();
+        Response::redirect("/dorms/{$dormId}");
+        return;
+      }
+      var_dump($form->getFields()["errors"]);
+      exit();
+    }
+    $userId = Request::getUser()->user_id;
+    $this->render("review/create", [
+      "userId" => $userId,
+      ...$form->getFields()
+    ]);
+  }
+
+  public function getReviewForm(){
+    return new Validation([
+      [new Field("rate", "Rating"), [new Required(), new Rating()]],
+      [new Field("description", "Deskripsi"), [new Required()]],
+    ]);
+  }
+
+  private function setReviewFormData(Validation $form, Reviews $review)
+  {
+    $form->data["rate"] = $review->rate;
+    $form->data["description"] = $review->description;
+  }
+
+  public function edit($params)
+  {
+    $reviewId = $params["reviewId"];
+    $review = Reviews::findById($reviewId);
+    $userId = $review->user_id;
+    $dormId = $review->dorm_id;
+    $formReview = $this->getReviewForm();
+
+    if (Request::getMethod() === "POST") {
+      if ($formReview->validate(Request::getBody())) {
+        $review = Reviews::toModel($formReview->data);
+        $review->review_id = $reviewId;
+        $review->user_id = $userId;
+        $review->dorm_id = $dormId;
+        $review->save();
+        Toast::success("Review berhasil diubah", true);
+        Response::redirect("/dorms/{$dormId}");
+        return;
+      }
+    } else {
+      if (Request::getUser()->user_id !== $userId) {
+        Response::redirect("/dorms/{$dormId}");
+        return;
+      }
+      $this->setReviewFormData($formReview, $review);
+    }
+    
+    $user = Request::getUser();
+    $this->render("review/create", [
+      "reviewId" => $reviewId,
+      ...$formReview->getFields()
+    ]);
+  }
+
+  public function delete($params)
+  {
+    $dormId = $params["dormId"];
+
+    $form = new Validation([
+      [new Field('review_id'), [new Number()]]
+    ]);
+
+    if ($form->validate(Request::getBody())) {
+      $reviewId = $form->data["review_id"];
+      Reviews::deleteById($reviewId);
+    }
+  
+    Toast::success("Review berhasil dihapus", true);
+    Response::redirect("/dorms/{$dormId}");
+  }
+}
diff --git a/src/Core/Application.php b/src/Core/Application.php
index 47cf365d2cfdcff700722ee31e4903b6d57828a9..1aea8de50f3bf686b3c532c48e78a25e6b271108 100644
--- a/src/Core/Application.php
+++ b/src/Core/Application.php
@@ -6,6 +6,7 @@ use app\Controllers\OwnerController;
 use app\Controllers\AuthController;
 use app\Controllers\DormController;
 use app\Controllers\UserController;
+use app\Controllers\ReviewController;
 use app\Middlewares\AdminOnly;
 use app\Middlewares\AuthRequired;
 use app\Models\BaseModel;
@@ -45,10 +46,16 @@ class Application
     $this->router->get("/me", [AuthRequired::class], AuthController::class, 'me');
 
     $this->router->methods(["GET", "POST"], "/dorms/create", [AdminOnly::class], DormController::class, 'create');
+    $this->router->methods(["GET", "POST"], "/dorms/{dormId}", [AuthRequired::class], DormController::class, 'view');
     $this->router->methods(["GET", "POST"], "/dorms/{dormId}/media", [AdminOnly::class], DormController::class, 'media');
     $this->router->methods(["GET", "POST"], "/dorms/{dormId}/edit", [AdminOnly::class], DormController::class, 'edit');
+    $this->router->methods(["GET", "POST"], "/dorms/{dormId}/reviews", [AuthRequired::class], ReviewController::class, 'getAllByDormId');
+    $this->router->methods(["GET", "POST"], "/dorms/{dormId}/add-review", [AuthRequired::class], ReviewController::class, 'create');
   
     $this->router->methods(["GET", "POST"], "/account/edit", [AuthRequired::class],  UserController::class, 'edit');
     $this->router->methods(["GET", "POST"], "/account/editPassword", [AuthRequired::class],  UserController::class, 'editPassword');
+    
+    $this->router->methods(["GET", "POST"], "/reviews/{reviewId}", [AuthRequired::class], ReviewController::class, 'edit');
+    $this->router->delete("/dorms/{dormId}", [AuthRequired::class], ReviewController::class, 'delete');
   }
 }
diff --git a/src/Forms/Rating.php b/src/Forms/Rating.php
new file mode 100644
index 0000000000000000000000000000000000000000..9167ca1ff04bb105098b6f545d9ab3f7836d9a64
--- /dev/null
+++ b/src/Forms/Rating.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace app\Forms;
+
+
+class Rating extends Rule
+{
+  public function validate(string $label, &$value): ?string
+  {
+    if (is_int($value) && 1 <= $value && $value <= 5) {
+      return null;
+    }
+
+    try {
+      $value = intval($value);
+      return null;
+    } catch (\Throwable $th) {
+      return "$label harus berupa angka 1 sampai 5";
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/Models/Reviews.php b/src/Models/Reviews.php
new file mode 100644
index 0000000000000000000000000000000000000000..f5fe8a6911fc5a71031182dd56bf4d1f8b246f06
--- /dev/null
+++ b/src/Models/Reviews.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace app\Models;
+
+class Reviews extends BaseModel
+{
+  public int $review_id;
+  public int $user_id;
+  public int $dorm_id;
+  public int $kos_id;
+  public int $rate;
+  public ?string $description;
+  public string $created_at;
+  protected static string $table = 'reviews';
+  protected static string $primaryKey = 'review_id';
+
+  public static function findByDormId(int $dormId)
+  {
+    return self::where(["dorm_id" => $dormId]);
+  }
+  
+}
diff --git a/src/Views/dorm/view.php b/src/Views/dorm/view.php
new file mode 100644
index 0000000000000000000000000000000000000000..b802a5cbbf44e02b4f39abaa258827166c964009
--- /dev/null
+++ b/src/Views/dorm/view.php
@@ -0,0 +1,130 @@
+@extends('layouts/base')
+@@head
+<link rel="stylesheet" href="/static/styles/dorm-view.css?v=<?php
+
+use app\Models\User;
+
+ echo time(); ?>">
+@@endhead
+<div class="container">
+  <? if (count($medias) > 0) : ?>
+    <section class="slider-wrapper">
+      <button class="slide-arrow slide-arrow-prev">
+        &#8249;
+      </button>
+
+      <button class="slide-arrow slide-arrow-next">
+        &#8250;
+      </button>
+
+
+      <ul class="slides-container">
+        <? foreach ($medias as $media) : ?>
+          <li class="slide">
+            <? if ($media->type == "video") : ?>
+              <video src="<?= $media->endpoint ?>" alt="<?= $media->alt_text ?>" controls></video>
+            <? else : ?>
+              <img src="<?= $media->endpoint ?>" alt="<?= $media->alt_text ?>">
+            <? endif; ?>
+          </li>
+        <?php endforeach; ?>
+      </ul>
+    </section>
+  <? endif; ?>
+
+  <div class="info">
+    <div class="info-dorm">
+      <h1 class="title"><?= $dorm->name ?></h1>
+      <h2 class="address"><?= $dorm->address ?></h2>
+      <h3 class="city"><?= $dorm->city ?></h3>
+      <h3 class="description"><?= $dorm->description ?></h3>
+    </div>
+
+
+    <div class="info-booking">
+
+      <p class="price">Rp<?= number_format($dorm->price, 0, ',', '.') ?>
+            <span> / bulan</span>
+      </p>
+
+      <p class="info-owner">Info Pemilik</p>
+      <p><span class="person-icon">&#128100;</span>: <?= $owner->name ?></p>
+      <p><span class="phone-icon">&nbsp;&#9742;</span>&nbsp;: <?= $owner->phone_number ?></p>
+    </div>
+  </div>
+
+  <? if ($user->is_admin ?? false) : ?>
+  <div class="edit-admin">
+    <a href="/dorms/<?= $dorm->dorm_id ?>/edit" class="btn btn-primary btn-edit-kos">Edit Kos</a>
+  </div>
+  <? endif; ?>
+  
+  <div class="reviews">
+    <h1> Review Kos Ini</h1>
+    <? if (!$user->is_admin ?? false) : ?>
+      <div class="add-review">
+        <a href="/dorms/<?= $dorm->dorm_id ?>/add-review" class="btn btn-primary btn-add-review">
+          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="3" stroke="currentColor"
+          width="12" height="12" aria-hidden="true">
+          <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
+          </svg>
+        Tambahkan Review
+        </a>
+      </div>
+    <? endif; ?>
+      
+    <ul class="review-list">
+      <? foreach ($reviews as $review) : ?>
+        <? $reviewer = User::findById($review->user_id); ?>
+        <li class="review-item">
+          <div class="review-item-content">
+            <h3 class="review-item-name"><?= $reviewer->name; ?></h3>
+            <p class="review-item-rate">
+              <?
+              $rating = $review->rate;
+              $stars = str_repeat("★", $rating); 
+              $emptyStars = str_repeat("☆", 5 - $rating); 
+              echo $stars . $emptyStars;
+              ?>
+            </p>
+            <p class="review-item-description"><?= $review->description ?></p>
+          </div>
+          <div class="review-actions">
+            <? if (($user->is_admin ?? false) || ($user->user_id == $reviewer->user_id  ?? false)) : ?>
+              <? if ($user->user_id == $reviewer->user_id ??  false) : ?>
+                <a href="/reviews/<?= $review->review_id ?>" class="btn btn-outlined edit-review" data-dialog="add-owner-dialog"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
+                  stroke-width="2" stroke="currentColor" width="20" height="20">
+                  <path stroke-linecap="round" stroke-linejoin="round"
+                  d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
+                  </svg>
+                </a>
+              <? endif ?>
+              <form method="POST">
+                <button type="button" class="btn btn-danger dialog-btn action-btn"
+                data-dialog="delete-<?= $review->review_id ?>"><svg xmlns="http://www.w3.org/2000/svg" fill="none"
+                viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" width="20" height="20">
+                <path stroke-linecap="round" stroke-linejoin="round"
+                d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
+                </svg>
+                </button>
+                <input type="hidden" name="review_id" value="<?= $review->review_id ?>" />
+                <input type="hidden" name="_method" value="DELETE" />
+                <div class="dialog-wrapper delete-<?= $review->review_id ?>">
+                  <div class="dialog-content">
+                    <h4 class="confirm-title">Apakah anda yakin ingin menghapus review?</h4>
+                    <div class="confirm-action">
+                      <button type="button" class="btn btn-outlined dialog-btn"
+                      data-dialog="delete-<?= $review->review_id ?>">Batal</button>
+                      <button type="submit" class="btn btn-danger">Hapus</button>
+                    </div>
+                  </div>
+                </div>
+              </form>
+            <? endif; ?>
+          </div>
+        </li>
+      <? endforeach; ?>
+    </ul>
+
+  </div>
+</div>
\ No newline at end of file
diff --git a/src/Views/review/create.php b/src/Views/review/create.php
new file mode 100644
index 0000000000000000000000000000000000000000..8eeee8e48c45664b6c86a42ac587699e336efe6a
--- /dev/null
+++ b/src/Views/review/create.php
@@ -0,0 +1,31 @@
+@extends('layouts/base')
+@@head
+<link rel="stylesheet" href="/static/styles/create-dorm.css">
+@@endhead
+<div>
+  <h1><?= isset($reviewId) ? "Edit Review" : "Tambahkan Review" ?></h1>
+  <form method="POST" novalidate class="create-form">
+    <div class="input-group">
+      <label for="rate"  class="required">Rating</label>
+      <select autofocus name="rate">
+        <?php
+        $ratings = [1, 2, 3, 4, 5];
+        foreach ($ratings as $rating) : 
+            $star = str_repeat("&#9734;", $rating);?>
+            <option value=<?=$rating?> <?if ($fields["rate"] === $rating) : ?> selected <? endif; ?>><?= $star?></option> 
+        <? endforeach; ?>
+      </select>
+    </div>
+    <div class="input-group">
+      <label for="description" class="required">Deskripsi</label>
+      <textarea name="description" placeholder="Deskripsi"><?= $fields["description"] ?? '' ?></textarea>
+      <?php
+      if (isset($errors) && array_key_exists('description', $errors)) {
+        echo '<div class="error">' . $errors["description"] . '</div>';
+      }
+      ?>
+    </div>
+    <button class="btn btn-primary">
+      <?= isset($reviewId) ? "Edit" : "Tambah" ?> Review
+  </form>
+</div>
\ No newline at end of file
diff --git a/src/public/static/styles/dorm-view.css b/src/public/static/styles/dorm-view.css
new file mode 100644
index 0000000000000000000000000000000000000000..c6244cee65f70aaf8495342a3486b52eec789671
--- /dev/null
+++ b/src/public/static/styles/dorm-view.css
@@ -0,0 +1,134 @@
+.container {
+    max-width: 1080px;
+    width: 100%;
+    margin: 0 auto;
+}
+.slides-container {
+    aspect-ratio: 16 / 9;
+  }
+
+.info {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  align-items: flex-start;
+}
+
+.info-dorm {
+  flex-basis: calc(65% - 20px);
+}
+
+.title {
+  font-size: 24px;
+  margin-bottom: 10px;
+}
+
+.address {
+  font-size: 18px;
+  margin-bottom: 5px;
+}
+
+.city {
+  font-size: 16px;
+  margin-bottom: 10px;
+}
+
+.description {
+  font-size: 16px;
+  line-height: 1.4;
+  margin-bottom: 10px;
+}
+
+.info-booking {
+  flex-basis: calc(35% - 20px);
+  background-color: #f5f5f5;
+  padding: 20px;
+  border-radius: 5px;
+  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+  transition: background-color 0.3s, box-shadow 0.3s;
+  position: sticky;
+  top: 100px;
+}
+
+.main-content {
+  margin-bottom: 100px;
+}
+
+.price {
+  font-size: 24px;
+  font-weight: bold;
+  margin-bottom: 10px;
+  color: var(--color-primary);
+}
+
+.info-owner{
+  font-weight: bold;
+}
+
+.person-icon {
+  display: inline-block;
+  margin-right: 5px;
+  font-size: 20px;
+  color: var(--color-primary);
+}
+.phone-icon {
+  display: inline-block;
+  margin-right: 5px;
+  font-size: 20px;
+  color: var(--color-primary);
+}
+
+.add-review,
+.edit-admin {
+  margin-top: 20px;
+  margin-bottom: 20px;
+}
+
+.btn-edit-kos,
+.btn-add-review {
+  display: inline-block;
+}
+
+ul.review-list {
+  list-style: none;
+  padding: 0;
+}
+
+li.review-item {
+  margin-top: 20px;
+  margin-bottom: 20px;
+  border: 1px solid #ccc;
+  padding: 10px;
+  background-color: #f9f9f9;
+  display: flex; 
+  justify-content: space-between;
+  align-items: center; 
+}
+
+div.review-item-content {
+  display: flex;
+  flex-direction: column;
+}
+
+h3.review-item-name {
+  margin: 0;
+  font-weight: bold;
+}
+
+p.review-item-rate {
+  margin: 5px 0;
+  font-weight: bold;
+  font-size: 16px;
+  color: #ff9900;
+}
+
+p.review-item-description {
+  margin: 5px 0;
+  font-size: 14px;
+  color: #333;
+}
+
+.review-actions {
+  display: flex;
+  gap: 10px;
+}