Educate - LMS Documentation

Educate Rails 7

Resources

Ruby version

Check ruby version (has to be at least 3.0.0).

ruby -v

Install ruby 3 via rbenv

rbenv install 3.0.0
rbenv local 3.0.0

Create an isolated, new Rails 7 app

echo "source 'https://rubygems.org'" > Gemfile
echo "gem 'rails', '7.0.0'" >> Gemfile
bundle install
bundle exec rails new . --force --css=bootstrap -d=postgresql

Create bare minimal Bootstrap 5 HTML

Controller

inside app/controllers/home_controller.rb

class HomeController < ApplicationController  
end

Configure a default route

inside config/routes.rb

Rails.application.routes.draw do  
  get "home/index"
  root to: "home#index"
end

View

app/views/home/index.html.erb

<div class="container-fluid page__heading-container">
  <div class="page__heading d-flex align-items-center">
    <div class="flex">
      <nav aria-label="breadcrumb">
        <ol class="breadcrumb mb-0">
          <li class="breadcrumb-item"><a href="#">Home</a></li>
          <li class="breadcrumb-item active" aria-current="page">Dashboard</li>
        </ol>
      </nav>
      <h1 class="m-0">Dashboard</h1>
    </div>
    <a href="" class="btn btn-success ms-3">New Report</a>
  </div>
</div>

<div class="page__container container-fluid"> 
  <button type="button" 
          class="btn btn-lg btn-danger" 
          data-toggle="sidebar" 
          data-target="#default-drawer">
          Toggle Drawer
  </button>  
</div>
  • Configuration

Database creation

rails db:create
rails db:migrate

Local Development

./bin/dev

Theme dependencies

package.json

{
  "dependencies": {
    "@fortawesome/fontawesome-free": "^5.4.1",
    "@fortawesome/fontawesome-svg-core": "^1.2.6",
    "@fortawesome/free-solid-svg-icons": "^6.0.0",
    "@fortawesome/vue-fontawesome": "^3.0.0-5",
    "@hotwired/stimulus": "^3.1.0",
    "@hotwired/turbo-rails": "^7.1.3",
    "@popperjs/core": "^2.11.6",
    "bootstrap": "^5.2.1",
    "bootstrap-icons": "^1.9.1",
    "chart.js": "^2.7.3",
    "daterangepicker": "^3.0.3",
    "dragula": "^3.7.2",
    "dropzone": "^5.5.1",
    "flatpickr": "^4.5.2",
    "fullcalendar": "3.9.0",
    "ion-rangeslider": "^2.3.0",
    "jquery": "^3.6.0",
    "jquery-mask-plugin": "^1.14.15",
    "jquery-ui-dist": "^1.12.1",
    "jqvmap": "github:10bestdesign/jqvmap#master",
    "list.js": "^1.5.0",
    "material-design-icons-iconfont": "^3.0.3",
    "material-design-kit": "^2.2.1",
    "moment": "^2.22.2",
    "moment-range": "^4.0.1",
    "perfect-scrollbar": "^1.4.0",
    "quill": "^1.3.6",
    "select2": "^4.0.6-rc.1",
    "sidebar-style-guide": "^2.0.0",
    "toastr": "^2.1.4"
  },
  "devDependencies": {
    "esbuild": "^0.15.7",
    "sass": "^1.54.9"
  }
}

Install

npm install

Theme Style

mkdir app/assets/stylesheets/educate
cp -rf ~/Code/educate/src/scss/* app/assets/stylesheets/educate/

app/assets/stylesheets/application.bootstrap.scss

// @import 'bootstrap/scss/bootstrap';
// @import 'bootstrap-icons/font/bootstrap-icons';

@import './educate/app.scss'

Theme JavaScript

mkdir app/javascript/educate
cp -rf ~/Code/educate/src/js/* app/javascript/educate/

app/javascript/application.js

// Entry point for the build script in your package.json
import "@hotwired/turbo-rails"
import "./controllers"

// import * as bootstrap from "bootstrap"
import "./educate"

Theme Images

mkdir app/assets/images/educate
cp -rf ~/Code/educate/src/images/* app/assets/images/educate

Theme Fonts

Add node_modules to assets paths in config/application.rb

# ...
config.load_defaults 7.0
config.assets.paths << Rails.root.join("node_modules")

Add a custom variables file app/stylesheets/_variables.scss

$material-design-icons-font-path: 'material-design-icons-iconfont/dist/fonts/' !default;

Load it from the main application stylesheet app/stylesheets/application.bootstrap.scss

@import './variables';
@import './educate/app';

Restart dev server

./bin/dev

Theme App Layout

app/controller/home_controller.rb

class HomeController < ApplicationController
  layout 'educate/application'
end

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

<!DOCTYPE html>
<html dir="ltr">
  <head>
    <title>RailsBs5Educate</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

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

  <body class="layout-default">
    <div class="preloader"></div>

    <!-- Header Layout -->
    <div class="mdk-header-layout js-mdk-header-layout">

      <!-- Header -->
      <div id="header" class="mdk-header js-mdk-header m-0" data-fixed>
        <div class="mdk-header__content">
          <%= render 'layouts/educate/navbar' %>
        </div>
      </div>
      <!-- // END Header -->

      <!-- Header Layout Content -->
      <div class="mdk-header-layout__content">

        <div class="mdk-drawer-layout js-mdk-drawer-layout" data-push data-responsive-width="992px">
          <div class="mdk-drawer-layout__content page">
            <%= yield %>
          </div>
          <!-- // END drawer-layout__content -->

          <%= render 'layouts/educate/drawer' %>
        </div>
        <!-- // END drawer-layout -->

      </div>
      <!-- // END header-layout__content -->

    </div>
    <!-- // END header-layout -->

    <%= content_for :footer %>
  </body>
</html>

app/views/layouts/educate/_navbar.html.erb

<div class="navbar navbar-expand-sm navbar-main navbar-dark bg-dark pe-0" id="navbar" data-primary>
  <div class="container-fluid p-0">

    <!-- Fluid / Mini Navbar toggler -->
    <%# <button class="navbar-toggler navbar-toggler-custom navbar-toggler-right d-block" type="button" data-toggle="sidebar">
      <span class="material-icons">apps</span>
    </button> %>

    <!-- App Navbar toggler -->
    <button class="navbar-toggler navbar-toggler-right d-block d-md-none" type="button" data-toggle="sidebar">
      <span class="navbar-toggler-icon"></span>
    </button>

    <!-- Navbar Brand -->
    <a href="" class="navbar-brand ms-2 me-3">
      <img class="navbar-brand-icon me-2" src="<%= image_path('educate/educate-logo-white.svg') %>" width="22" alt="Educate">
      <span>Educate 2</span>
    </a>

    <form class="search-form d-none d-sm-flex flex" action="">
      <button class="btn" type="submit" role="button"><i class="material-icons">search</i></button>
      <input type="text" class="form-control" placeholder="Search">
    </form>

    <div class="flex"></div>

    <ul class="nav navbar-nav ms-auto d-none d-md-flex">
      <li class="nav-item">
        <a href="" class="nav-link">
          <i class="material-icons">help_outline</i> Get Help
        </a>
      </li>
      <li class="nav-item me-3">
        <a href="" class="btn btn-outline-warning">
          <i class="material-icons">star</i> PRO
        </a>
      </li>
      <li class="nav-item dropdown">
        <a href="#notifications_menu" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" data-caret="false">
          <i class="material-icons nav-icon navbar-notifications-indicator">notifications</i>
        </a>
        <div id="notifications_menu" class="dropdown-menu dropdown-menu-end navbar-notifications-menu">
          <div class="dropdown-item d-flex align-items-center py-2">
            <span class="flex navbar-notifications-menu__title m-0">Notifications</span>
            <a href="javascript:void(0)" class="text-muted"><small>Clear all</small></a>
          </div>
          <div class="navbar-notifications-menu__content" data-perfect-scrollbar>
            <div class="py-2">
              <% [1,2,3].each do %>
                <div class="dropdown-item d-flex">
                  <div class="me-3">
                    <div class="avatar avatar-sm" style="width: 32px; height: 32px;">
                      <img src="<%= image_path('educate/256_daniel-gaffey-1060698-unsplash.jpg') %>" alt="Avatar" class="avatar-img rounded-circle">
                    </div>
                  </div>
                  <div class="flex">
                    <a href="">A.Demian</a> left a comment on <a href="">Educate</a><br>
                    <small class="text-muted">1 minute ago</small>
                  </div>
                </div>
                <div class="dropdown-item d-flex">
                  <div class="me-3">
                    <a href="#">
                      <div class="avatar avatar-xs" style="width: 32px; height: 32px;">
                        <span class="avatar-title bg-purple rounded-circle"><i class="material-icons icon-16pt">person_add</i></span>
                      </div>
                    </a>
                  </div>
                  <div class="flex">
                    New user <a href="#">Peter Parker</a> signed up.<br>
                    <small class="text-muted">1 hour ago</small>
                  </div>
                </div>
                <div class="dropdown-item d-flex">
                  <div class="me-3">
                    <a href="#">
                      <div class="avatar avatar-xs" style="width: 32px; height: 32px;">
                        <span class="avatar-title rounded-circle">JD</span>
                      </div>
                    </a>
                  </div>
                  <div class="flex">
                    <a href="#">Big Joe</a> <small class="text-muted">wrote:</small><br>
                    <div>Hey, how are you? What about our next meeting</div>
                    <small class="text-muted">2 minutes ago</small>
                  </div>
                </div>
              <% end %>
            </div>
          </div>
          <a href="javascript:void(0);" class="dropdown-item text-center navbar-notifications-menu__footer">View All</a>
        </div>
      </li>
    </ul>

    <ul class="nav navbar-nav d-none d-sm-flex border-left navbar-height align-items-center">
      <li class="nav-item dropdown">
        <a href="#account_menu" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" data-caret="false">
          <img src="<%= image_path('educate/avatar/demi.png') %>" class="rounded-circle" width="32" alt="Frontted">
          <span class="ms-1 d-flex-inline">
            <span class="text-light">Adrian D.</span>
          </span>
        </a>
        <div id="account_menu" class="dropdown-menu dropdown-menu-end">
          <div class="dropdown-item-text dropdown-item-text--lh">
            <div><strong>Adrian Demian</strong></div>
            <div>@adriandemian</div>
          </div>
          <div class="dropdown-divider"></div>
          <a class="dropdown-item" href="">Dashboard</a>
          <a class="dropdown-item" href="">My profile</a>
          <a class="dropdown-item" href="">Edit account</a>
          <div class="dropdown-divider"></div>
          <a class="dropdown-item" href="">Logout</a>
        </div>
      </li>
    </ul>

  </div>
</div>

app/views/layouts/educate/_drawer.html.erb

<div class="mdk-drawer js-mdk-drawer" id="default-drawer" data-align="start">
  <div class="mdk-drawer__content">
    <div class="sidebar sidebar-light sidebar-left" data-perfect-scrollbar>
      <div class="d-flex align-items-center sidebar-p-a border-bottom sidebar-account">
        <a href="" class="flex d-flex align-items-center text-underline-0 text-body">
          <span class="avatar me-3">
            <img src="<%= image_path('educate/avatar/demi.png') %>" alt="avatar" class="avatar-img rounded-circle">
          </span>
          <span class="flex d-flex flex-column">
            <strong>Adrian Demian</strong>
            <small class="text-muted text-uppercase">Account Manager</small>
          </span>
        </a>
        <div class="dropdown ml-auto">
          <a href="#" data-bs-toggle="dropdown" data-caret="false" class="text-muted"><i class="material-icons">more_vert</i></a>
          <div class="dropdown-menu dropdown-menu-end">
            <div class="dropdown-item-text dropdown-item-text--lh">
              <div><strong>Adrian Demian</strong></div>
              <div>@adriandemian</div>
            </div>
            <div class="dropdown-divider"></div>
            <a class="dropdown-item" href="">Dashboard</a>
            <a class="dropdown-item" href="">My profile</a>
            <a class="dropdown-item" href="">Edit account</a>
            <div class="dropdown-divider"></div>
            <a class="dropdown-item" href="">Logout</a>
          </div>
        </div>
      </div>

      <div class="sidebar-p-a sidebar-b-y">
        <div class="d-flex align-items-top mb-2">
          <div class="sidebar-heading m-0 p-0 flex text-body js-text-body">Progress</div>
          <div class="fw-bold text-success">60%</div>
        </div>
        <div class="progress">
          <div class="progress-bar bg-success" role="progressbar" style="width: 60%" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"></div>
        </div>
      </div>
      <div class="sidebar-heading sidebar-m-t">Menu</div>
      <div class="sidebar-block p-0">
        <ul class="sidebar-menu">
          <li class="sidebar-menu-item active">
            <a class="sidebar-menu-button" href="">
              <i class="sidebar-menu-icon sidebar-menu-icon--left material-icons">dvr</i>
              <span class="sidebar-menu-text">Dashboard</span>
            </a>
          </li>

          <li class="sidebar-menu-item">
            <a class="sidebar-menu-button" data-bs-toggle="collapse" href="#layouts_menu" role="button" aria-expanded="false" aria-controls="layouts_menu">
              <i class="sidebar-menu-icon sidebar-menu-icon--left material-icons">view_compact</i>
              <span class="sidebar-menu-text">Layouts</span>
              <span class="ms-auto sidebar-menu-toggle-icon"></span>
            </a>
            <ul class="sidebar-submenu collapse" id="layouts_menu">
              <li class="sidebar-menu-item active">
                <a class="sidebar-menu-button" href="">
                  <span class="sidebar-menu-text">App Layout</span>
                </a>
              </li>
              <li class="sidebar-menu-item">
                <a class="sidebar-menu-button" href="">
                  <span class="sidebar-menu-text">Fluid</span>
                </a>
              </li>
              <li class="sidebar-menu-item">
                <a class="sidebar-menu-button" href="">
                  <span class="sidebar-menu-text">Fixed</span>
                </a>
              </li>
              <li class="sidebar-menu-item">
                <a class="sidebar-menu-button" href="">
                  <span class="sidebar-menu-text">Mini</span>
                </a>
              </li>
            </ul>
          </li>
        </ul>
      </div>
    </div>
  </div>
</div>

Adding a custom theme page Alerts

Add route to config/routes.rb

get "home/alerts", as: "alerts"

Add controller action to app/controllers/home_controller.rb

class HomeController < ApplicationController  
  def alerts
  end
end

Add menu item to app/views/layouts/educate/_drawer.html.erb

<div class="sidebar-heading sidebar-m-t">UI Components</div>
<ul class="sidebar-menu">
  <li class="sidebar-menu-item <% if current_page?(alerts_path) %>active<% end%>">
    <a class="sidebar-menu-button" href="<%= alerts_path %>">
      <i class="sidebar-menu-icon sidebar-menu-icon--left material-icons">notifications</i>
      <span class="sidebar-menu-text">Alerts</span>
    </a>
  </li>
</ul>

Add view to app/views/home/alerts.html.erb

<div class="container-fluid page__heading-container">
  <div class="page__heading d-flex align-items-center">
    <div class="flex">
      <nav aria-label="breadcrumb">
        <ol class="breadcrumb mb-0">
          <li class="breadcrumb-item"><a href="#">Home</a></li>
          <li class="breadcrumb-item active" aria-current="page">UI Components</li>
        </ol>
      </nav>
      <h1 class="m-0">Alerts</h1>
    </div>
  </div>
</div>

<div class="page__container container-fluid"> 
  <button 
    type="button" 
    class="btn btn-primary"
    data-toggle="toastr"
    data-toastr-type="primary"
    data-toastr-title="Heads up!"
    data-toastr-message="This alert needs your attention, but it is not super important.">
    Click me
  </button>
</div>

<% content_for :header do %>
<%= stylesheet_link_tag "toastr" %>
<%= javascript_include_tag "plugins/toastr", defer: true %>
<% end %>

Link toastr from app/assets/config/manifest.js

//= link toastr/build/toastr.min.css

Update package.json to compile files from app/javascript/educate/plugins

{
  "scripts": {
    "build": "esbuild app/javascript/**/**/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets"
  }
}

Remove .vue files from app/javascript/** as esbuild is not configured yet to handle Vue

Remove or comment out the contents of app/javascript/educate/page.ui-icons.js as its loading .vue files

Wrap initializing code from app/javascript/plugins/toastr.js into document.addEventListener("turbo:load", () => {

import $ from 'jquery'
import toastr from 'toastr'

document.addEventListener("turbo:load", () => {
  $('[data-toggle="toastr"]').on('click', function (e) {
    // sample code
  })
});

Adding the Full Width Navs layout

app/controllers/home_controller.rb

class HomeController < ApplicationController
  before_action :set_layout
  layout :layout_by

  private

  def set_layout
    if params[:layout].present?
      session[:layout] = "educate/#{params[:layout]}"
    end

    @layout = session[:layout] ||= "educate/application"

    if @layout == "educate/application"
      @containerClass = 'container-fluid'
      @navbarContainerClass = 'container-fluid'
      @bodyClass = 'layout-default'

    elsif @layout == "educate/fluid"
      @containerClass = 'container-fluid'
      @navbarContainerClass = 'container-fluid p-0'
      @bodyClass = 'layout-fluid layout-sticky-subnav'
    end
  end

  def layout_by
    @layout
  end
end

app/views/layouts/educate/fluid.html.erb

<!DOCTYPE html>
<html dir="ltr">
  <head>
    <title>Full Width Navs | Educate 2 - Rails 7, Bootstrap 5</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

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

  <% @containerClass ||= 'container-fluid' %>
  <% @navbarContainerClass ||= 'container-fluid p-0' %>
  <% @bodyClass ||= 'layout-fluid layout-sticky-subnav' %>

  <body class="<%= @bodyClass %>">
    <%# <div class="preloader"></div> %>

    <!-- Header Layout -->
    <div class="mdk-header-layout js-mdk-header-layout">

      <!-- Header -->
      <div id="header" class="mdk-header bg-dark js-mdk-header m-0" data-fixed>
        <div class="mdk-header__content">
          <%= render 'layouts/educate/navbar' %>
        </div>
      </div>
      <!-- // END Header -->

      <!-- Header Layout Content -->
      <div class="mdk-header-layout__content page">
        <%= yield %>
      </div>
      <!-- // END header-layout__content -->

    </div>
    <!-- // END header-layout -->

    <%= render 'layouts/educate/drawer' %>

    <%= content_for :footer %>
  </body>
</html>

Replace the following in app/views/layouts/educate/application.html.erb

<body class="layout-default">

With:

<% @containerClass ||= 'container-fluid' %>
<% @navbarContainerClass ||= 'container-fluid' %>
<% @bodyClass ||= 'layout-default' %>

<body class="<%= @bodyClass %>">
railsthemes.com

Rails Admin Themes for your Rails Application Dashboard

Get in touch