Repository: awesome-academy/naitei_ruby_10_02_2025_hotel_management
Pull request #6: static page
Author: login: lamvqh-3644
Created: 2025-03-26T18:25:37Z | Merged: 2025-03-27T03:49:31Z

## Related Tickets
- [#TicketID](https://edu-redmine.sun-asterisk.vn/issues/86049?issue_count=21&issue_position=19&next_issue_id=86048&prev_issue_id=86051)

## WHAT (optional)
- Change number items `completed/total` in admin page.

## HOW
- I edit js file, inject not_vary_normal items in calculate function.

## WHY (optional)
- Because in previous version - number just depends on `normal` items. But in new version, we have `state` and `confirm_state` depends on both `normal` + `not_normal` items.

## Evidence (Screenshot or Video)
![image](https://github.com/user-attachments/assets/dba73ff9-48b1-4e25-bfdb-cc69401cf69a)



Diff:
diff --git a/Gemfile b/Gemfile
index b969611..ead5eac 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,10 +10,9 @@ gem "importmap-rails"
 gem "mysql2", "~> 0.5"
 gem "puma", "~> 5.0"
 gem "rails", "~> 7.0.5"
+gem "sassc"
 gem "sprockets-rails"
 gem "stimulus-rails"
-gem "tailwindcss-rails", "~> 4.2"
-gem "tailwindcss-ruby", "~> 4.0"
 gem "turbo-rails"
 gem "tzinfo-data", platforms: %i(mingw mswin x64_mingw jruby)
 group :development, :test do
@@ -31,3 +30,5 @@ group :test do
   gem "selenium-webdriver"
   gem "webdrivers"
 end
+gem "tailwindcss-rails", "~> 4.2"
+gem "tailwindcss-ruby", "~> 4.0"
diff --git a/Gemfile.lock b/Gemfile.lock
index 1237e24..90a18d8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -70,7 +70,7 @@ GEM
       public_suffix (>= 2.0.2, < 6.0)
     ast (2.4.2)
     base64 (0.1.1)
-    bcrypt (3.1.20)
+    bcrypt (3.1.13)
     bindex (0.8.1)
     bootsnap (1.16.0)
       msgpack (~> 1.2)
@@ -96,6 +96,7 @@ GEM
     deep_merge (1.2.2)
     diff-lcs (1.6.0)
     erubi (1.12.0)
+    ffi (1.17.1-x86_64-linux-gnu)
     figaro (1.2.0)
       thor (>= 0.14.0, < 2)
     globalid (1.1.0)
@@ -222,6 +223,8 @@ GEM
       rubocop (>= 1.7.0, < 2.0)
     ruby-progressbar (1.13.0)
     rubyzip (2.3.2)
+    sassc (2.4.0)
+      ffi (~> 1.9)
     selenium-webdriver (4.10.0)
       rexml (~> 3.2, >= 3.2.5)
       rubyzip (>= 1.2.2, < 3.0)
@@ -284,6 +287,7 @@ DEPENDENCIES
   rubocop (~> 1.26)
   rubocop-checkstyle_formatter
   rubocop-rails (~> 2.14.0)
+  sassc
   selenium-webdriver
   sprockets-rails
   stimulus-rails
diff --git a/app/assets/images/sheraton_1.jpg b/app/assets/images/sheraton_1.jpg
new file mode 100644
index 0000000..117a644
Binary files /dev/null and b/app/assets/images/sheraton_1.jpg differ
diff --git a/app/assets/images/sheraton_2.jpg b/app/assets/images/sheraton_2.jpg
new file mode 100644
index 0000000..cd6d12a
Binary files /dev/null and b/app/assets/images/sheraton_2.jpg differ
diff --git a/app/assets/images/sheraton_3.jpg b/app/assets/images/sheraton_3.jpg
new file mode 100644
index 0000000..cd3a070
Binary files /dev/null and b/app/assets/images/sheraton_3.jpg differ
diff --git a/app/assets/images/sheraton_4.jpg b/app/assets/images/sheraton_4.jpg
new file mode 100644
index 0000000..f6ede7b
Binary files /dev/null and b/app/assets/images/sheraton_4.jpg differ
diff --git a/app/assets/stylesheets/_variables.scss b/app/assets/stylesheets/_variables.scss
new file mode 100644
index 0000000..5dae399
--- /dev/null
+++ b/app/assets/stylesheets/_variables.scss
@@ -0,0 +1,13 @@
+$white-color: #fff;
+$background-light: #f8f9fa;
+$text-color: #444;
+$text-muted: #666;
+$text-light: #888;
+$primary-color: #007bff;
+$primary-hover: #0056b3;
+$accent-color: #ff6600;
+$accent-hover: #e65c00;
+$warning-color: #f39c12;
+$price-old: #aaa;
+$price-new: #e74c3c;
+$border-color: #eee;
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
deleted file mode 100644
index 288b9ab..0000000
--- a/app/assets/stylesheets/application.css
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * This is a manifest file that'll be compiled into application.css, which will include all the files
- * listed below.
- *
- * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
- * vendor/assets/stylesheets directory can be referenced here using a relative path.
- *
- * You're free to add application-wide styles to this file and they'll appear at the bottom of the
- * compiled file so the styles you add here take precedence over styles defined in any other CSS
- * files in this directory. Styles in this file should be added after the last require_* statement.
- * It is generally better to create a new file per style scope.
- *
- *= require_tree .
- *= require_self
- */
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
new file mode 100644
index 0000000..1889f22
--- /dev/null
+++ b/app/assets/stylesheets/application.scss
@@ -0,0 +1,2 @@
+@import 'custom';
+@import 'variables';
diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss
new file mode 100644
index 0000000..72c4127
--- /dev/null
+++ b/app/assets/stylesheets/custom.scss
@@ -0,0 +1,161 @@
+@import 'variables';
+
+.hotel-card {
+    max-width: 900px;
+    margin: 2rem auto;
+    padding: 1.5rem;
+    background: $white-color;
+    border-radius: 16px;
+    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+    font-family: "Helvetica", sans-serif;
+
+    .images {
+        display: flex;
+        gap: 1rem;
+
+        .main-image {
+            width: 60%;
+            border-radius: 12px;
+            object-fit: cover;
+        }
+
+        .sub-images {
+            display: flex;
+            flex-direction: column;
+            gap: 0.5rem;
+
+            img {
+                width: 300px;
+                height: 120px;
+                object-fit: cover;
+                border-radius: 8px;
+            }
+        }
+    }
+
+    .hotel-info {
+        margin-top: 1rem;
+
+        .rating {
+            font-size: 1rem;
+            color: $warning-color;
+        }
+
+        .hotel-name {
+            font-size: 1.5rem;
+            margin: 0.2rem 0;
+        }
+
+        .address {
+            color: $text-muted;
+        }
+    }
+
+    .promotion {
+        margin-top: 1rem;
+        background: $background-light;
+        padding: 1rem;
+        border-radius: 12px;
+        font-size: 0.95rem;
+        color: $text-color;
+    }
+
+    .price-box {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-top: 1rem;
+
+        .time {
+            font-size: 0.9rem;
+            color: $text-light;
+        }
+
+        .price {
+            .old {
+                text-decoration: line-through;
+                color: $price-old;
+                margin-right: 0.5rem;
+            }
+
+            .new {
+                font-size: 1.4rem;
+                font-weight: bold;
+                color: $price-new;
+            }
+        }
+    }
+
+    .action {
+        margin-top: 1rem;
+        text-align: right;
+
+        .btn-choose {
+            background-color: $accent-color;
+            color: $white-color;
+            padding: 0.75rem 1.5rem;
+            border: none;
+            border-radius: 12px;
+            font-size: 1rem;
+            cursor: pointer;
+            transition: background-color 0.3s;
+
+            &:hover {
+                background-color: $accent-hover;
+            }
+        }
+    }
+}
+
+.site-header {
+    background: $white-color;
+    border-bottom: 1px solid $border-color;
+    padding: 1rem 0;
+    font-family: "Helvetica", sans-serif;
+
+    .container {
+        max-width: 1000px;
+        margin: 0 auto;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 0 1rem;
+    }
+
+    .logo a {
+        text-decoration: none;
+        font-weight: bold;
+        font-size: 1.4rem;
+        color: #333;
+    }
+
+    .right-menu {
+        display: flex;
+        align-items: center;
+        gap: 1.5rem;
+
+        .language-switch a {
+            text-decoration: none;
+            color: $text-muted;
+            font-weight: 500;
+
+            &.active {
+                color: $primary-color;
+                font-weight: bold;
+            }
+        }
+
+        .login-link a {
+            background-color: $primary-color;
+            color: $white-color;
+            padding: 0.5rem 1rem;
+            border-radius: 8px;
+            text-decoration: none;
+            transition: background 0.3s;
+
+            &:hover {
+                background-color: $primary-hover;
+            }
+        }
+    }
+}
diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb
new file mode 100644
index 0000000..e401cb0
--- /dev/null
+++ b/app/controllers/static_pages_controller.rb
@@ -0,0 +1,3 @@
+class StaticPagesController < ApplicationController
+  def home; end
+end
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index c95923c..685fe15 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -6,14 +6,11 @@
     <%= csrf_meta_tags %>
     <%= csp_meta_tag %>
     <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
-
     <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
     <%= javascript_importmap_tags %>
   </head>
-
   <body>
+    <%= render "shared/header" %>
     <%= yield %>
-    <%= link_to "English", locale:"en" %>
-    <%= link_to "Vietnamese", locale:"vi" %>
   </body>
 </html>
diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb
deleted file mode 100644
index cbd34d2..0000000
--- a/app/views/layouts/mailer.html.erb
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    <style>
-      /* Email styles need to be inline */
-    </style>
-  </head>
-
-  <body>
-    <%= yield %>
-  </body>
-</html>
diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb
deleted file mode 100644
index 37f0bdd..0000000
--- a/app/views/layouts/mailer.text.erb
+++ /dev/null
@@ -1 +0,0 @@
-<%= yield %>
diff --git a/app/views/shared/_header.html.erb b/app/views/shared/_header.html.erb
new file mode 100644
index 0000000..1bac320
--- /dev/null
+++ b/app/views/shared/_header.html.erb
@@ -0,0 +1,16 @@
+<header class="site-header">
+  <div class="container">
+    <div class="logo">
+      <%= link_to I18n.t('logo'), root_path %>
+    </div>
+    <div class="right-menu">
+      <div class="language-switch">
+        <%= link_to I18n.t('language.vietnamese'), url_for(locale: :vi), class: (I18n.locale == :vi ? 'active' : '') %> |
+        <%= link_to I18n.t('language.english'), url_for(locale: :en), class: (I18n.locale == :en ? 'active' : '') %>
+      </div>
+      <div class="login-link">
+        <%= link_to I18n.t('login'), "#" %>
+      </div>
+    </div>
+  </div>
+</header>
diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb
new file mode 100644
index 0000000..3418270
--- /dev/null
+++ b/app/views/static_pages/home.html.erb
@@ -0,0 +1,36 @@
+<div class="hotel-card">
+  <div class="images">
+    <img src="<%= asset_path("sheraton_1.jpg") %>" class="main-image" alt="Sheraton Hanoi Hotel">
+    <div class="sub-images">
+      <img src="<%= asset_path("sheraton_2.jpg") %>" alt="Sheraton Hanoi Hotel">
+      <img src="<%= asset_path("sheraton_3.jpg") %>" alt="Sheraton Hanoi Hotel">
+       <img src="<%= asset_path("sheraton_4.jpg") %>" alt="Sheraton Hanoi Hotel">
+    </div>
+  </div>
+
+  <div class="hotel-info">
+    <div class="rating">
+      <%= I18n.t("hotel.rating") %>
+    </div>
+    <h2 class="hotel-name"><%= I18n.t("hotel.name") %></h2>
+    <p class="address"><%= I18n.t("hotel.address") %></p>
+  </div>
+
+  <div class="promotion">
+    <p><%= I18n.t("hotel.promotion") %></p>
+  </div>
+
+  <div class="price-box">
+    <div class="time">
+        <%= I18n.t "time.duration" %> | <%= I18n.t "time.period" %>
+    </div>
+    <div class="price">
+      <span class="old"><%= I18n.t("hotel.price.old") %></span>
+      <span class="new"><%= I18n.t("hotel.price.new") %></span>
+    </div>
+  </div>
+
+  <div class="action">
+    <button class="btn-choose"><%= I18n.t("hotel.button") %></button>
+  </div>
+</div>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a9f72ec..799ad09 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1,2 +1,19 @@
 en:
-  hello: "Hello world"
+  hello: "Hello"
+  language:
+    english: "English"
+    vietnamese: "Vietnamese"
+  login: "Login"
+  logo: "🏨 Hotel Booking"
+  hotel:
+    name: "Lam Hotel"
+    rating: "⭐ 4.5 (132)"
+    address: "97 Khuc Thua Du Street, Dich Vong, Cau Giay, Hanoi"
+    promotion: "🎁50% off up to 60K, minimum order 200K"
+    price:
+      old: "180.000đ"
+      new: "99.000đ"
+    button: "Choose room"
+  time:
+    duration: "02 hours"
+    period: "08:00 → 10:00, 26/03"
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index abf3730..3400ded 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -1,2 +1,20 @@
+
 vi:
   hello: "Xin chào"
+  language:
+    english: "Tiếng Anh"
+    vietnamese: "Tiếng Việt"
+  login: "Đăng nhập"
+  logo: "🏨 Đặt phòng khách sạn"
+  hotel:
+    name: "Lam Hotel"
+    rating: "⭐ 4.5 (132)"
+    address: "97 Phố Khúc Thừa Dụ, Dịch Vọng, Cầu Giấy, Hà Nội"
+    promotion: "🎁 Giảm 50% tối đa 60K, đặt tối thiểu 200K"
+    price:
+      old: "180.000đ"
+      new: "99.000đ"
+    button: "Chọn phòng"
+  time:
+    duration: "02 giờ"
+    period: "08:00 → 10:00, 26/03"
diff --git a/config/routes.rb b/config/routes.rb
index 1daf9a4..176ab4e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,2 +1,6 @@
 Rails.application.routes.draw do
+  scope "(:locale)", locale: /en|vi/ do
+    root "static_pages#home"
+    get "static_pages/home"
+  end
 end


Reviews:
body: cmt
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-27T01:32:03Z
body: cmt
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-27T03:01:17Z
body: cmt
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-27T03:16:37Z
body: merged
state: COMMENTED
author_association: NONE
submitted_at: 2025-03-27T03:49:26Z

Review comments:
diff_hunk: @@ -1,2 +1,4 @@
 Rails.application.routes.draw do
-end
+    root "pages#home"
+    get "static_pages/home"
path: config/routes.rb
body: Thêm scope của i18n vào em ơi
created_at: 2025-03-27T01:31:58Z
author_association: NONE
diff_hunk: @@ -1,2 +1,5 @@
 Rails.application.routes.draw do
-end
+    scope "(:locale)", locale: /en|vi/ do
+     root "pages#home"
+     get "static_pages/home"
path: config/routes.rb
body: Sai indent rồi em
created_at: 2025-03-27T03:01:11Z
author_association: NONE
diff_hunk: @@ -1,2 +1,6 @@
 Rails.application.routes.draw do
-end
+    scope "(:locale)", locale: /en|vi/ do
+      root "pages#home"
+      get "static_pages/home"
+    end
+  end
path: config/routes.rb
body: Vẫn sai indent mà đã đúng đâu em
created_at: 2025-03-27T03:16:31Z
author_association: NONE

PR comments:
user: login: lamvqh-3644
created_at: 2025-03-26T18:50:59Z
body: ready
author_association: CONTRIBUTOR
user: login: lamvqh-3644
created_at: 2025-03-27T02:59:17Z
body: ready
author_association: CONTRIBUTOR