Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[管理画面] 管理画面へのログイン機能、管理画面トップページの作成 #281

Closed
Itok1000 opened this issue Jan 15, 2025 · 0 comments

Comments

@Itok1000
Copy link
Owner

Itok1000 commented Jan 15, 2025

実現したいこと

管理画面へのログイン機能、管理画面トップページの作成したい

その後、下記のように対応
管理画面におけるマイグレとモデルの準備
#295
管理画面におけるルート側とコントローラーの実装
#300
管理画面のビュー(view)作成
#305
指定の JavaScript パッケージをインストールを取りやめて、既存のCSSを再利用
#308

Users テーブルに role カラムを追加

Users テーブルに role カラムを追加するマイグレーションファイルを作成するためにターミナルにて Users テーブルに role カラムを追加するマイグレーションファイルを生成するコマンドを実行する

rails generate migration AddRoleToUsers role:integer

生成されたマイグレーションファイルは以下のように編集

class AddRoleToUsers < ActiveRecord::Migration[7.2]
  def change
    add_column :users, :role, :integer, null: false, default: 0
  end
end

上記が反映されることで Users テーブルに role カラムが追加されることに加えて、 role カラムにはデフォルトで 0 が登録される

User モデルに enum を定義

app/models/user.rb を以下のように編集

class User < ApplicationRecord
# 省略
  has_many 
# 省略
  enum role: { general: 0, admin: 1 }
# 省略
end

上記の記述で role カラム(integer)の値に応じて、そのユーザーが general(一般)か admin(管理者)が判別できるようになる
先程のマイグレーションファイルの default: 0 によって role カラムが 0 のユーザーは general となる

role を admin に変更する

以下のコマンドをターミナルにて実行して、Rails コンソールを立ち上げ、現在ログインしているユーザーの role を admin に変更する

# docker compose upが実行されているターミナルとは別のターミナルを用意して以下を実行する

$ docker compose exec web rails c

- user = User.find_by(email: 'xxxxx')   # xxxxxには、現在ログインしているユーザーのメールアドレスを記入
- user.role   # 現在ログインしているユーザーの role を確認
- user.admin!   # 現在ログインしているユーザーの role を admin に変更

# 上記操作で現在ログインしているユーザーの role が admin になっているかを確認
- user = User.find_by(email: 'xxxxx')
- user.role

管理画面(admin)を切り出す

管理画面関連の URL・コントローラー・ビュー・アセット・etc を、以下のようにして一般ユーザー触れる部分と切り分ける

コントローラー:app/controllers/配下にadminディレクトリを作成し、管理画面関連はそちらのディレクトリ配下に配置する。
ビュー:app/views/配下にadminディレクトリを作成し、管理画面関連はそちらのディレクトリ配下に配置する。
URL:namespaceを使ってadminを切り出し、管理画面関連はそちらに配置する。
アセット:管理画面用と分かるように整理・配置する。

今回はまずコントローラーとURLの対応を行う

管理画面は /admin をルートとするように設定する(URL:namespaceを使ってadminを切り出し、管理画面関連はそちらに配置する。)

config/routes.rb を以下のように編集

namespace :admin do
    root "dashboards#index"
    resource :dashboard, only: %i[index]
    get 'login' => 'user_sessions#new', :as => :login
    post 'login' => "user_sessions#create"
    delete 'logout' => 'user_sessions#destroy', :as => :logout
  end
  # 上記の namespace :admin の記載により以下が定義される
  # Helper	HTTP verb	Path	コントローラー#アクション
  # admin_root_path	GET	/admin	admin/dashboards#index
  # admin_login_path	GET	/admin/login	admin/user_sessions#new
  # admin_login_path	POST	/admin/login	admin/user_sessions#create
  # admin_logout_path	DELETE	/admin/logout	admin/user_sessions#destroy

app/controllers/配下にadminディレクトリを作成し、管理画面関連はそちらのディレクトリ配下に配置する

1. app/controllers/admin/base_controller.rb を生成・編集

以下のコマンドをターミナルにて実行する

$ mkdir app/controllers/admin
$ touch app/controllers/admin/base_controller.rb

生成された app/controllers/admin/base_controller.rb を以下のように編集する

class Admin::BaseController < ApplicationController
  before_action :check_admin
  layout 'admin/layouts/application'

  private

  def not_authenticated
    flash[:warning] = t('defaults.flash_message.require_login')
    redirect_to admin_login_path
  end

  def check_admin
    redirect_to root_path, danger: t('defaults.flash_message.not_authorized') unless current_user.admin?
  end
end

上記の記述をした app/controllers/admin/base_controller.rb を作成し、app/controllers/admin/配下に作成するコントローラーは app/controllers/admin/base_controller.rb を継承させることで before_action :check_admin, layout 'admin/layouts/application', not_authenticated メソッドも継承する

check_admin メソッドは、ログインしているユーザーの role カラムが admin で無かった場合に指定のパスにリダイレクトさせる

not_authenticated メソッドは ApplicationController で定義されている not_authenticated を上書きしている

layout 'admin/layouts/application'の記述で、app/views/admin/layouts/配下にある application.html.erb をレイアウトとして使用する

2. app/controllers/admin/user_sessions_controller.rb を生成・編集

以下のコマンドをターミナルにて実行する

$ touch app/controllers/admin/user_sessions_controller.rb

生成された app/controllers/admin/user_sessions_controller.rb を以下のように編集する

class Admin::UserSessionsController < Admin::BaseController
  skip_before_action :require_login, only: %i[new create]
  skip_before_action :check_admin, only: %i[new create]
  layout 'layouts/admin_login'

  def new; end

  def create
    @user = login(params[:email], params[:password])
    if @user
      redirect_to admin_root_path, success: t('.success')
    else
      flash.now[:danger] = t('.failure')
      render :new
    end
  end

  def destroy
    logout
    redirect_to admin_login_path, status: :see_other, danger: t('.success')
  end
end

Admin::UserSessionsController は、Admin::BaseController を継承しているため、ApplicationController の require_login メソッドも継承している(Admin::BaseController は ApplicationController を継承しているため)

layout 'layouts/admin_login' の記述によって、Admin::UserSessionsController のアクションは、app/views/layouts/配下にある admin_login.html.erb が使用される

3. app/controllers/admin/dashboards_controller.rbを生成・編集

以下のコマンドをターミナルにて実行する

$ touch app/controllers/admin/dashboards_controller.rb

生成された aapp/controllers/admin/dashboards_controller.rb を以下のように編集する

class Admin::DashboardsController < Admin::BaseController
    before_action :check_admin
    layout 'admin/layouts/application'

    def index; end
end

Admin::DashboardsController は、Admin::BaseController を継承しているため、ApplicationController の require_login メソッドも継承している(Admin::DashboardsController は ApplicationController を継承しているため)

layout 'layouts/admin_login' の記述によって、Admin::UserSessionsController のアクションは、app/views/admin/dashboards/配下にある index.html.erb が使用される

管理画面へログインした後のダッシュボードページ画面の作成する

app/views/admin/dashboards/index.html.erb

<% content_for(:title, t('.title')) %>
<div class="container">
  <div class="row">
    ダッシュボード
  </div>
</div>

全管理ページで共通のレイアウトを定義する

app/views/admin/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title><%= page_title(yield(:title), admin: true) %></title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "admin/application" %>
    <%= javascript_include_tag "admin", "data-turbo-track": "reload", defer: true %>
  </head>

  <body>
    <%= render 'admin/shared/header' %>
    <div class="container-fluid">
      <div class="row">
        <%= render 'admin/shared/sidebar' %>
        <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
          <%= render 'shared/flash_message' %>
          <%= yield %>
        </main>
      </div>
    </div>
  </body>
</html>

管理ページのフッターを定義する

app/views/admin/shared/_footer.html.erb

<footer>
  <p class='mt-3 text-muted'>© 2024. Itok1000.</p>
</footer>

管理ページのヘッダーを定義する

app/views/admin/shared/_header.html.erb

<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
  <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6" href="#">
    ガマルジョバ
  </a>
  <div class="w-100">
    <button class="navbar-toggler d-none d-md-block" type="button" id='toggleSidebar'>
      <span class="navbar-toggler-icon"></span>
    </button>
    <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
  </div>
  <div class="navbar-nav">
    <div class="nav-item text-nowrap">
      <%= link_to t('header.logout'), admin_logout_path, data: { turbo_method: :delete }, local: true, class: 'nav-link px-3' %>
    </div>
  </div>
</header>

管理ページのサイドバーを定義する

app/views/admin/shared/_sidebar.html.erb

<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
  <div class="position-sticky pt-3 sidebar-sticky">
    <ul class="nav flex-column">
      <li class="nav-item">
        <%= link_to '#', class: 'nav-link' do %>
          <i class="bi bi-file-earmark"></i>
          <%= User.model_name.human %>
        <% end %>
      </li>
      <li class="nav-item">
        <%= link_to '#', class: 'nav-link' do %>
          <i class="bi bi-person"></i>
          <%= Board.model_name.human %>
        <% end %>
      </li>
    </ul>
  </div>
</nav>

ログインページのview画面を作成する

app/views/admin/user_sessions/new.html.erb

<% content_for(:title, t('.title')) %>
<main class="form-signin w-100 m-auto">
  <%= render 'shared/flash_message' %>
  <%= form_with url: admin_login_path, data: { turbo: false } do |f| %>
    <h1 class="h3 mb-3 fw-normal"><%= t('.title') %></h1>
    <div class="form-floating">
      <%= f.email_field :email, class: "form-control", placeholder: "[email protected]" %>
      <%= f.label :email%>
    </div>
    <div class="form-floating">
      <%= f.password_field :password, class: "form-control", placeholder: "Password" %>
      <%= f.label :password %>
    </div>
    <%= f.submit t('.login'), class: "w-100 btn btn-lg btn-primary" %>
  <% end %>
  <%= render 'admin/shared/footer'%>
</main>

管理画面のログインページのレイアウトページを作成する

app/views/layouts/admin_login.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title><%= page_title(yield(:title), admin: true) %></title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag 'admin/login' %>
    <%= javascript_include_tag "admin", "data-turbo-track": "reload", defer: true %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

page_title メソッド修正する

現状ローカルホストで開くと、下記のArgumentエラーが発生する
page_titleメソッドに渡される引数の数が期待される数と合っていないから発生してるので、コードを見直して、page_titleメソッドを呼び出している部分を確認する必要がある

Image from Gyazo

キーワード引数を追加し、admin の値が ture or false によって base_title が動的に変化するように修正

module ApplicationHelper
    def page_title(title = '', admin: false)
        base_title = admin ? 'ガマルジョバ/გამარჯობა(管理画面)' : 'ガマルジョバ/გამარჯობა'
        title.present? ? "#{title} | #{base_title}" : base_title
    end
end

ロゴマークを持ってくる

app/assets/images/AdminLTELogo.pngをコピーして張りつける

application.jsにadminLTE情報を追加

下記の資料のscriptとhrefをapp/views/admin/layouts/application.html.erbに格納する
Railsのapplication.html.erbファイルは、全てのページで共有される共通のレイアウトを定義するためのファイル
今回の管理画面のJavascriptも全ての管理者ページで共有できるようにするため、公式のscriptとhrefを格納

https://adminlte.io/docs/3.2/

app/views/admin/layouts/application.html.erb

<head>
<--省略-->
<%= stylesheet_link_tag "admin/application" %>
    <%= javascript_include_tag "admin", "data-turbo-track": "reload", defer: true %>

    <%= display_meta_tags %>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/adminlte.min.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@3.2/dist/css/adminlte.min.css">
</head>

管理画面関連で使用する JavaScript のマニフェストファイルを生成・編集する
管理画面関連で使用する JavaScript のマニフェストファイル(app/javascript/admin.js)を生成して、以下のように編集

$ touch app/javascript/admin.js
import "@hotwired/turbo-rails"
import * as bootstrap from "bootstrap/dist/js/bootstrap"
import 'admin-lte/dist/js/adminlte'

@hotwired/turbo-rails, bootstrap/dist/js/bootstrap を使用する部分は、app/javascript/application.js と同じだが、admin_teq/assets/dist/js/dashboard の一文を追加
これにより app/javascript/admin.js を読み込むと、node_modules/admin-lte/dist/js/配下にある adminlted.js も読み込まれる

管理画面関連で使用する stylesheet のマニフェストファイルを生成・編集する

管理画面関連で使用する styleshhet のマニフェストファイル(app/assets/stylesheets/admin/application.scss, app/assets/stylesheets/admin/login.scss)を生成する

$ mkdir app/assets/stylesheets/admin
$ touch app/assets/stylesheets/admin/application.scss
$ touch app/assets/stylesheets/admin/login.scss

app/assets/stylesheets/admin/application.scss は以下のように編集

@import "application.bootstrap";
@import "admin-lte/dist/css/adminlte";

app/assets/stylesheets/admin/login.scss は以下のように編集

@import "application.bootstrap";
@import "admin-lte/dist/css/adminlte";

dartsass の設定

config/initializers/dartsass.rb を生成して以下のように編集

Rails.application.config.dartsass.builds = {
  'application.scss' => 'application.css',
  'admin/application.scss' => 'admin/application.css',
  'admin/login.scss' => 'admin/login.css'
}

対応を変更

stylesheet のマニフェストファイルとdartsass の設定を削除

管理画面関連で使用する styleshhet のマニフェストファイル(app/assets/stylesheets/admin/application.scss, app/assets/stylesheets/admin/login.scss)を削除
また、フッターも不要なので、フッターも削除

app/views/admin/shared/_footer.html.erb
javascript/admin.js
admin/login.scss
stylesheets/admin/login.scss
admin/application.scss
config/initializers/dartsass.rb

application.jsにadminLTE情報を追加を取りやめ

下記の資料のscriptとhrefをapp/views/admin/layouts/application.html.erbに格納するのを取りやめ
https://adminlte.io/docs/3.2/

app/views/admin/layouts/application.html.erb

<head>
<--省略-->
<%= stylesheet_link_tag "admin/application" %>
    <%= javascript_include_tag "admin", "data-turbo-track": "reload", defer: true %>

    <%= display_meta_tags %>
  <-- ここから -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/adminlte.min.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/adminlte.min.css">
       <-- ここまでを削除 -->
</head>

アセットパイプラインの紐づけを変更

stylesheet のマニフェストファイルを削除したことにより、アセットパイプラインが内のエラーが起きたため、
app/views/layouts/admin_login.html.erbとapp/views/admin/layouts/application.html.erbは
既存アプリのマニフェストファイルを再利用する方向に修正
Before

<%= stylesheet_link_tag "admin/application" %>

After

<%= stylesheet_link_tag "application" %>

管理画面のログインview画面のフォーマットを作成する

app/views/admin/user_sessions/new.html.erbを編集し、
既存アプリのcssフォーマットを再利用して、view画面を整える

<% content_for(:title, t('.title')) %>
<-- ここから -->
<div class="top-wrapper">
 <div class="container">
  <div class="row">
    <div class="col-md-10 col-lg-8 mx-auto">
      <div class='text-center'>
<-- ここまでのdiv classのcssを再利用 -->
      <main class="form-signin w-100 m-auto">
      <%= render 'shared/flash_message' %>
      <%= form_with url: admin_login_path, data: { turbo: false } do |f| %>
        <h1 class="h3 mb-3 fw-normal"><%= t('.title') %></h1>
        <div class="form-floating">
          <%= f.email_field :email, class: "form-control", placeholder: "[email protected]" %>
          <%= f.label :email%>
        </div>
        <br>
        <div class="form-floating">
          <%= f.password_field :password, class: "form-control", placeholder: "Password" %>
          <%= f.label :password %>
        </div>
        <br>
        <%= f.submit t('.login'), class: "w-100 btn btn-lg btn-primary" %>
      <% end %>
     </main>
<-- ここから -->
      </div>
    </div>
  </div>
 </div>
</div>
<-- ここまでのdiv classのcssを再利用 -->

i18nの翻訳エラーの修正

改行漏れによる翻訳不可エラーを解消するため、
config/locales/view/ja.ymlの「admin:」の改行を追加し、タイトルを「管理者画面ログイン」に修正変更

動作の確認を行う

Image from Gyazo
Image from Gyazo

参考にしたURL・使用したプロンプト

https://qiita.com/shouta-dev/items/2d46c68ebbe163dc0c6c
https://github.com/ColorlibHQ/AdminLTE

@Itok1000 Itok1000 moved this to 本リリース後の運用 in 卒業制作 Jan 15, 2025
@Itok1000 Itok1000 moved this from 本リリース後の運用 to Done in 卒業制作 Feb 17, 2025
@Itok1000 Itok1000 closed this as completed by moving to Done in 卒業制作 Feb 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

No branches or pull requests

1 participant