diff --git a/.rspec b/.rspec
new file mode 100644
index 00000000..c99d2e73
--- /dev/null
+++ b/.rspec
@@ -0,0 +1 @@
+--require spec_helper
diff --git a/Gemfile b/Gemfile
index ce35e710..4458447c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -16,8 +16,6 @@ gem 'image_processing', '~> 1.2'
gem 'jbuilder' # Build JSON APIs with ease [https://github.com/rails/jbuilder]
gem 'jsbundling-rails' # Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]
gem 'mini_magick', '~> 4.12'
-# Motor Admin allows you to deploy a no-code admin panel for your application in less than a minute
-gem 'motor-admin', '~> 0.4.7'
gem 'pg', '~> 1.1' # Use postgresql as the database for Active Record
gem 'premailer-rails', '~> 1.12' # This gem is a drop in solution for styling HTML emails with CSS
gem 'puma', '~> 5.0' # Use the Puma web server [https://github.com/puma/puma]
@@ -66,7 +64,11 @@ group :test do
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
gem 'capybara'
gem 'faker', '~> 3.1'
+ gem 'rspec-rails', '~> 6.1'
gem 'selenium-webdriver'
gem 'simplecov', require: false # Code coverage analysis tool for ruby
gem 'webdrivers'
end
+
+gem 'rails_admin', '~> 3.1'
+gem 'sassc-rails'
diff --git a/Gemfile.lock b/Gemfile.lock
index cd1df74a..d01e491d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -56,6 +56,10 @@ GEM
globalid (>= 0.3.6)
activemodel (7.0.3)
activesupport (= 7.0.3)
+ activemodel-serializers-xml (1.0.2)
+ activemodel (> 5.x)
+ activesupport (> 5.x)
+ builder (~> 3.1)
activerecord (7.0.3)
activemodel (= 7.0.3)
activesupport (= 7.0.3)
@@ -75,11 +79,7 @@ GEM
public_suffix (>= 2.0.2, < 5.0)
airbrussh (1.4.1)
sshkit (>= 1.6.1, != 1.7.0)
- ar_lazy_preload (1.1.2)
- rails (>= 5.2)
ast (2.4.2)
- audited (5.3.2)
- activerecord (>= 5.0, < 7.1)
aws-eventstream (1.2.0)
aws-partitions (1.721.0)
aws-sdk-core (3.170.0)
@@ -149,19 +149,15 @@ GEM
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
+ diff-lcs (1.5.1)
digest (3.1.0)
docile (1.4.0)
dockerfile-rails (1.2.5)
rails
erubi (1.10.0)
- et-orbi (1.2.7)
- tzinfo
faker (3.1.0)
i18n (>= 1.8.11, < 2)
ffi (1.15.5)
- fugit (1.8.1)
- et-orbi (~> 1, >= 1.2.7)
- raabro (~> 1.4)
globalid (1.0.0)
activesupport (>= 5.0)
htmlentities (4.3.4)
@@ -180,6 +176,18 @@ GEM
jsbundling-rails (1.0.2)
railties (>= 6.0.0)
json (2.6.3)
+ kaminari (1.2.2)
+ activesupport (>= 4.1.0)
+ kaminari-actionview (= 1.2.2)
+ kaminari-activerecord (= 1.2.2)
+ kaminari-core (= 1.2.2)
+ kaminari-actionview (1.2.2)
+ actionview
+ kaminari-core (= 1.2.2)
+ kaminari-activerecord (1.2.2)
+ activerecord
+ kaminari-core (= 1.2.2)
+ kaminari-core (1.2.2)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.8.1)
@@ -195,13 +203,8 @@ GEM
mini_magick (4.12.0)
mini_mime (1.1.2)
minitest (5.15.0)
- motor-admin (0.4.7)
- ar_lazy_preload (~> 1.0)
- audited (~> 5.0)
- cancancan (~> 3.0)
- fugit (~> 1.0)
- rails (>= 5.2)
msgpack (1.5.2)
+ nested_form (0.3.2)
net-imap (0.2.3)
digest
net-protocol
@@ -242,7 +245,6 @@ GEM
public_suffix (4.0.7)
puma (5.6.4)
nio4r (~> 2.0)
- raabro (1.4.0)
racc (1.6.0)
rack (2.2.3.1)
rack-test (1.1.0)
@@ -266,6 +268,12 @@ GEM
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.2)
loofah (~> 2.3)
+ rails_admin (3.1.2)
+ activemodel-serializers-xml (>= 1.0)
+ kaminari (>= 0.14, < 2.0)
+ nested_form (~> 0.3)
+ rails (>= 6.0, < 8)
+ turbo-rails (~> 1.0)
railties (7.0.3)
actionpack (= 7.0.3)
activesupport (= 7.0.3)
@@ -283,6 +291,23 @@ GEM
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.5)
+ rspec-core (3.13.0)
+ rspec-support (~> 3.13.0)
+ rspec-expectations (3.13.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.13.0)
+ rspec-mocks (3.13.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.13.0)
+ rspec-rails (6.1.2)
+ actionpack (>= 6.1)
+ activesupport (>= 6.1)
+ railties (>= 6.1)
+ rspec-core (~> 3.13)
+ rspec-expectations (~> 3.13)
+ rspec-mocks (~> 3.13)
+ rspec-support (~> 3.13)
+ rspec-support (3.13.1)
rubocop (1.51.0)
json (~> 2.3)
parallel (~> 1.10)
@@ -307,6 +332,14 @@ GEM
ffi (~> 1.12)
ruby_http_client (3.5.5)
rubyzip (2.3.2)
+ sassc (2.4.0)
+ ffi (~> 1.9)
+ sassc-rails (2.1.2)
+ railties (>= 4.0.0)
+ sassc (>= 2.0)
+ sprockets (> 3.0)
+ sprockets-rails
+ tilt
selenium-webdriver (4.2.0)
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2, >= 3.2.5)
@@ -346,6 +379,7 @@ GEM
railties (>= 6.0.0)
strscan (3.0.3)
thor (1.2.1)
+ tilt (2.3.0)
timeout (0.3.0)
turbo-rails (1.1.1)
actionpack (>= 6.0.0)
@@ -401,15 +435,17 @@ DEPENDENCIES
jsbundling-rails
letter_opener
mini_magick (~> 4.12)
- motor-admin (~> 0.4.7)
pg (~> 1.1)
premailer-rails (~> 1.12)
puma (~> 5.0)
rails (~> 7.0.3)
+ rails_admin (~> 3.1)
redis (~> 4.0)
+ rspec-rails (~> 6.1)
rubocop (~> 1.51.0)
rubocop-performance (~> 1.18)
rubocop-rails (~> 2.19.1)
+ sassc-rails
selenium-webdriver
sendgrid-actionmailer (~> 3.2)
simple_form (~> 5.1)
diff --git a/app/assets/images/learningmaterialsthumbnail.jpeg b/app/assets/images/learningmaterialsthumbnail.jpeg
new file mode 100644
index 00000000..a672a2f1
Binary files /dev/null and b/app/assets/images/learningmaterialsthumbnail.jpeg differ
diff --git a/app/assets/stylesheets/mailgun_mails.css b/app/assets/stylesheets/mailgun_mails.css
index 11f9a3d5..c828fda3 100644
--- a/app/assets/stylesheets/mailgun_mails.css
+++ b/app/assets/stylesheets/mailgun_mails.css
@@ -9,6 +9,15 @@
font-size: 14px;
}
+/* Add CSS variable for repetitive colors */
+
+:root {
+ --red: #D0021B;
+ --lightblue: #348eda;
+ --orange: #ff9f00;
+ --lightgreen: #68B90F;
+ }
+
img {
max-width: 100%;
}
@@ -136,15 +145,15 @@ p li, ul li, ol li {
LINKS & BUTTONS
------------------------------------- */
a {
- color: #348eda;
+ color: var(--lightblue);
text-decoration: underline;
}
.btn-primary {
text-decoration: none;
color: #FFF;
- background-color: #348eda;
- border: solid #348eda;
+ background-color: var(--lightblue);
+ border: solid var(--lightblue);
border-width: 10px 20px;
line-height: 2em;
/* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
@@ -160,9 +169,9 @@ a {
.btn-warning {
text-decoration: none;
color: #FFF;
- background-color: #ff9f00;
- border: solid #ff9f00;
- border-color: #ff9f00;
+ background-color: var(--orange);
+ border: solid var(--orange);
+ border-color: var(--orange);
border-width: 10px 20px;
line-height: 2em;
/* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
@@ -178,9 +187,9 @@ a {
.btn-success {
text-decoration: none;
color: #FFF;
- background-color: #68B90F;
- border: solid #68B90F;
- border-color: #68B90F;
+ background-color: var(--lightgreen);
+ border: solid var(--lightgreen);
+ border-color: var(--lightgreen);
border-width: 10px 20px;
line-height: 2em;
/* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
@@ -196,9 +205,9 @@ a {
.btn-danger {
text-decoration: none;
color: #FFF;
- background-color: #D0021B;
- border: solid #D0021B;
- border-color: #D0021B;
+ background-color: var(--red);
+ border: solid var(--red);
+ border-color: var(--red);
border-width: 10px 20px;
line-height: 2em;
/* 2em * 14px = 28px, use px to get airier line-height also in Thunderbird, and Yahoo!, Outlook.com, AOL webmail clients */
@@ -257,13 +266,13 @@ a {
font-size: 16px;
}
.alert.alert-warning {
- background-color: #FF9F00;
+ background-color: var(--orange);
}
.alert.alert-bad {
- background-color: #D0021B;
+ background-color: var(--red);
}
.alert.alert-good {
- background-color: #68B90F;
+ background-color: var(--lightgreen);
}
/* -------------------------------------
diff --git a/app/controllers/learning_materials_controller.rb b/app/controllers/learning_materials_controller.rb
new file mode 100644
index 00000000..7b1912a0
--- /dev/null
+++ b/app/controllers/learning_materials_controller.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class LearningMaterialsController < ApplicationController
+ skip_before_action :authenticate_user!, only: %i[index]
+
+ def index
+ @learning_materials = LearningMaterial.includes(:thumbnail_blob).all
+ @learning_materials_with_thumbnails = @learning_materials.select do |learning_material|
+ learning_material.thumbnail.attached?
+ end
+ @random_learning_materials = @learning_materials_with_thumbnails.sample(2)
+ @learning_materials_with_or_without_thumbnails = @learning_materials - @random_learning_materials
+ end
+end
diff --git a/app/helpers/learning_materials_helper.rb b/app/helpers/learning_materials_helper.rb
new file mode 100644
index 00000000..7bef30b0
--- /dev/null
+++ b/app/helpers/learning_materials_helper.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+module LearningMaterialsHelper
+end
diff --git a/app/models/learning_material.rb b/app/models/learning_material.rb
new file mode 100644
index 00000000..25e8fe7f
--- /dev/null
+++ b/app/models/learning_material.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class LearningMaterial < ApplicationRecord
+ # Associations
+ belongs_to :user
+ has_one_attached :thumbnail
+
+ # Enum
+ enum tag: { beginners: 0, intermediates: 1, experts: 2 }
+
+ # Validations
+ validates :tag, :learning_material_title, :learning_material_description, :learning_material_link, presence: true
+ validates :learning_material_link,
+ format: { with: URI::DEFAULT_PARSER.make_regexp(%w[http https]), message: :invalid_url }
+end
diff --git a/app/models/motor/ability.rb b/app/models/motor/ability.rb
deleted file mode 100644
index 6596b366..00000000
--- a/app/models/motor/ability.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-##
-# CanCan permissions for motor_admin
-module Motor
- class Ability
- include CanCan::Ability
-
- def initialize(user, _request)
- case user.role
- when 'organization_admin'
- can :manage, :all
- end
- end
- end
-end
diff --git a/app/models/user.rb b/app/models/user.rb
index 614c5056..5ed48e99 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -10,6 +10,7 @@ class User < ApplicationRecord
# Associations
has_many :users_chapters, dependent: :nullify
has_many :chapters, through: :users_chapters
+ has_many :learning_materials, dependent: :nullify
# Callbacks
before_create :set_defaults # Set model defaults before create
diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb
index 1b99ece6..14cf70de 100644
--- a/app/views/layouts/_footer.html.erb
+++ b/app/views/layouts/_footer.html.erb
@@ -21,7 +21,7 @@
<%= link_to 'Chapters', chapters_path %>
- <%= link_to 'Learning materials', landing_learn_path %>
+ <%= link_to 'Learning materials', learning_materials_path %>
<% if FeatureFlag.find_by(name: 'projects').try(:enabled) %>
<%= link_to 'Projects', '#' %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index d074c1bf..7968e3f9 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -44,7 +44,7 @@
Projects
<% end %>
- <%= link_to 'Learning Materials', landing_learn_path %>
+ <%= link_to 'Learning Materials', learning_materials_path %>
<% if user_signed_in? %>
<%= button_to "Sign out", destroy_user_session_path, method: :delete %>
@@ -85,7 +85,7 @@
<% end %>
- <%= link_to 'Learning Materials', landing_learn_path,
+ <%= link_to 'Learning Materials', learning_materials_path,
class: 'text-sm font-small text-gray-500 hover:text-red-600' %>
<% if user_signed_in? %>
diff --git a/app/views/learning_materials/index.html.erb b/app/views/learning_materials/index.html.erb
new file mode 100644
index 00000000..252ff041
--- /dev/null
+++ b/app/views/learning_materials/index.html.erb
@@ -0,0 +1,94 @@
+<% content_for(:title,"Learning Materials") %>
+<% content_for(:description,"A list of ruby and ruby on rails learning resources compiled by the ARC community") %>
+
+
+
+
Featured Materials
+
+
+
+ <% if @learning_materials.empty? %>
+
There are no created learning materials yet
+ <% else %>
+ <% # Display the selected random records with thumbnails %>
+ <% @random_learning_materials.each do |learning_material| %>
+
+
+
+
+
+
+
+ <%= learning_material.created_at.strftime("%b %d") %>
+
+
+
+
<%= learning_material.learning_material_title %>
+
<%= learning_material.tag %>
+
+
+
<%= learning_material.learning_material_description %>
+
+
+
+ <%= link_to "READ MORE HERE", learning_material.learning_material_link, class: "learning_materialslink ml-1", target: "_blank", style: "color: #D0021B;" %>
+
+
+
+ <% end %>
+ <% end %>
+
+
+
+
Other Resources
+
+
+ <% # Display the remaining records regardless of whether it has thumbnail or not %>
+
+ <% @learning_materials_with_or_without_thumbnails.each do |learning_material| %>
+
+
+
+ <% if learning_material.thumbnail.attached? %>
+
+
+
+ <% else %>
+
+
+
+ <% end %>
+
+ <%= learning_material.created_at.strftime("%b %d") %>
+
+
+
+
<%= learning_material.learning_material_title %>
+
<%= learning_material.tag %>
+
+
+
<%= learning_material.learning_material_description %>
+
+
+
+ <%= link_to "READ MORE HERE", learning_material.learning_material_link, class: "learningmaterialslink ml-1", target: "_blank", style: "color: #D0021B;" %>
+
+
+
+ <% end %>
+
+
+
+
If you may not want the view above, here is a list of the same details shown above.
+
+
+
+ <% @learning_materials.each do |learning_material| %>
+ -
+
+ <%= link_to learning_material.learning_material_title, learning_material.learning_material_link, class: "learningmaterialslink ml-1", target: "_blank", style: "color: #D0021B;" %> - <%= learning_material.learning_material_description %>
+
+
+ <% end %>
+
+
\ No newline at end of file
diff --git a/config/initializers/rails_admin.rb b/config/initializers/rails_admin.rb
new file mode 100644
index 00000000..9283c387
--- /dev/null
+++ b/config/initializers/rails_admin.rb
@@ -0,0 +1,50 @@
+RailsAdmin.config do |config|
+ config.asset_source = :sprockets
+
+ ### Popular gems integration
+
+ ## == Devise ==
+ # config.authenticate_with do
+ # warden.authenticate! scope: :user
+ # end
+ # config.current_user_method(&:current_user)
+
+ ## == CancanCan ==
+ # config.authorize_with :cancancan
+
+ ## == Pundit ==
+ # config.authorize_with :pundit
+
+ ## == PaperTrail ==
+ # config.audit_with :paper_trail, 'User', 'PaperTrail::Version' # PaperTrail >= 3.0.0
+
+ ### More at https://github.com/railsadminteam/rails_admin/wiki/Base-configuration
+
+ ## == Gravatar integration ==
+ ## To disable Gravatar integration in Navigation Bar set to false
+ # config.show_gravatar = true
+
+ config.actions do
+ dashboard # mandatory
+ index # mandatory
+ new
+ export
+ bulk_delete
+ show
+ edit
+ delete
+ show_in_app
+
+ ## With an audit adapter, you can add:
+ # history_index
+ # history_show
+ end
+
+ # Configuring rails admin to use cancancan for authorization
+ config.authorize_with :cancancan
+ config.authorize_with do
+ unless current_user && current_user.organization_admin?
+ redirect_to main_app.root_path, alert: 'You are not authorized to access this page.'
+ end
+ end
+end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index bb6f3755..c7fba7de 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -29,4 +29,8 @@ en:
destroy:
success: 'Project was successfully destroyed.'
+ errors:
+ messages:
+ invalid_url: "must be a valid URL"
+
diff --git a/config/motor.yml b/config/motor.yml
deleted file mode 100644
index 53e043d0..00000000
--- a/config/motor.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-engine_version: 0.4.7
-file_version: 2023-03-09 16:16:17.698355000 Z
-resources:
-- name: chapter
- preferences:
- columns:
- - column_type: richtext
- name: description
- updated_at: 2023-03-05 09:31:13.962854000 +00:00
-- name: feature_flag
- preferences:
- columns:
- - column_type: richtext
- name: description
- updated_at: 2023-03-09 16:16:17.698355000 +00:00
-configs:
-- key: header.links
- value:
- - name: Reports
- link_type: reports
- - name: Forms
- link_type: forms
- - conditions: []
- type: header
- name: Exit admin portal
- path: "/"
- link_type: header
- updated_at: 2023-03-05 09:37:51.799389000 +00:00
-queries: []
-dashboards: []
-forms: []
-alerts: []
-api_configs:
-- id: 1
- name: origin
- url: "/"
- preferences: {}
- description:
- updated_at: 2023-03-05 08:41:14.200426000 +00:00
diff --git a/config/routes.rb b/config/routes.rb
index 0a04f5e9..846be344 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,12 +1,11 @@
# frozen_string_literal: true
Rails.application.routes.draw do
- authenticate :user do
- mount Motor::Admin => '/admin'
- end
+ mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
resources :projects, only: %i[index show]
resources :chapters, only: %i[index show]
resources :countries, only: %i[index show]
+ resources :learning_materials, only: %i[index show]
devise_for :users, controllers: {
registrations: 'users/registrations' # Override devise registration controller
}
@@ -16,5 +15,4 @@
root 'landing#index'
get 'about_us', to: 'landing#about', as: :landing_about
- get 'learn', to: 'landing#learn', as: :landing_learn
end
diff --git a/db/migrate/20230305084113_install_motor_admin.rb b/db/migrate/20230305084113_install_motor_admin.rb
deleted file mode 100644
index 97821239..00000000
--- a/db/migrate/20230305084113_install_motor_admin.rb
+++ /dev/null
@@ -1,268 +0,0 @@
-class InstallMotorAdmin < ActiveRecord::Migration[7.0]
- def self.up
- create_table :motor_queries do |t|
- t.column :name, :string, null: false
- t.column :description, :text
- t.column :sql_body, :text, null: false
- t.column :preferences, :text, null: false
- t.column :author_id, :bigint
- t.column :author_type, :string
- t.column :deleted_at, :datetime
-
- t.timestamps
-
- t.index :updated_at
- t.index 'name',
- name: 'motor_queries_name_unique_index',
- unique: true,
- where: 'deleted_at IS NULL'
- end
-
- create_table :motor_dashboards do |t|
- t.column :title, :string, null: false
- t.column :description, :text
- t.column :preferences, :text, null: false
- t.column :author_id, :bigint
- t.column :author_type, :string
- t.column :deleted_at, :datetime
-
- t.timestamps
-
- t.index :updated_at
- t.index 'title',
- name: 'motor_dashboards_title_unique_index',
- unique: true,
- where: 'deleted_at IS NULL'
- end
-
- create_table :motor_forms do |t|
- t.column :name, :string, null: false
- t.column :description, :text
- t.column :api_path, :text, null: false
- t.column :http_method, :string, null: false
- t.column :preferences, :text, null: false
- t.column :author_id, :bigint
- t.column :author_type, :string
- t.column :deleted_at, :datetime
- t.column :api_config_name, :string, null: false
-
- t.timestamps
-
- t.index :updated_at
- t.index 'name',
- name: 'motor_forms_name_unique_index',
- unique: true,
- where: 'deleted_at IS NULL'
- end
-
- create_table :motor_resources do |t|
- t.column :name, :string, null: false, index: { unique: true }
- t.column :preferences, :text, null: false
-
- t.timestamps
-
- t.index :updated_at
- end
-
- create_table :motor_configs do |t|
- t.column :key, :string, null: false, index: { unique: true }
- t.column :value, :text, null: false
-
- t.timestamps
-
- t.index :updated_at
- end
-
- create_table :motor_alerts do |t|
- t.references :query, null: false, foreign_key: { to_table: :motor_queries }, index: true
- t.column :name, :string, null: false
- t.column :description, :text
- t.column :to_emails, :text, null: false
- t.column :is_enabled, :boolean, null: false, default: true
- t.column :preferences, :text, null: false
- t.column :author_id, :bigint
- t.column :author_type, :string
- t.column :deleted_at, :datetime
-
- t.timestamps
-
- t.index :updated_at
- t.index 'name',
- name: 'motor_alerts_name_unique_index',
- unique: true,
- where: 'deleted_at IS NULL'
- end
-
- create_table :motor_alert_locks do |t|
- t.references :alert, null: false, foreign_key: { to_table: :motor_alerts }
- t.column :lock_timestamp, :string, null: false
-
- t.timestamps
-
- t.index %i[alert_id lock_timestamp], unique: true
- end
-
- create_table :motor_tags do |t|
- t.column :name, :string, null: false
-
- t.timestamps
-
- t.index 'name',
- name: 'motor_tags_name_unique_index',
- unique: true
- end
-
- create_table :motor_taggable_tags do |t|
- t.references :tag, null: false, foreign_key: { to_table: :motor_tags }, index: true
- t.column :taggable_id, :bigint, null: false
- t.column :taggable_type, :string, null: false
-
- t.index %i[taggable_id taggable_type tag_id],
- name: 'motor_polymorphic_association_tag_index',
- unique: true
- end
-
- create_table :motor_audits do |t|
- t.column :auditable_id, :string
- t.column :auditable_type, :string
- t.column :associated_id, :string
- t.column :associated_type, :string
- t.column :user_id, :bigint
- t.column :user_type, :string
- t.column :username, :string
- t.column :action, :string
- t.column :audited_changes, :text
- t.column :version, :bigint, default: 0
- t.column :comment, :text
- t.column :remote_address, :string
- t.column :request_uuid, :string
- t.column :created_at, :datetime
- end
-
- create_table :motor_api_configs do |t|
- t.column :name, :string, null: false
- t.column :url, :string, null: false
- t.column :preferences, :text, null: false
- t.column :credentials, :text, null: false
- t.column :description, :text
- t.column :deleted_at, :datetime
-
- t.timestamps
-
- t.index 'name',
- name: 'motor_api_configs_name_unique_index',
- unique: true,
- where: 'deleted_at IS NULL'
- end
-
- create_table :motor_notes do |t|
- t.column :body, :text
- t.column :author_id, :bigint
- t.column :author_type, :string
- t.column :record_id, :string, null: false
- t.column :record_type, :string, null: false
- t.column :deleted_at, :datetime
-
- t.timestamps
-
- t.index %i[author_id author_type],
- name: 'motor_notes_author_id_author_type_index'
- end
-
- create_table :motor_note_tags do |t|
- t.column :name, :string, null: false
-
- t.timestamps
-
- t.index 'name',
- name: 'motor_note_tags_name_unique_index',
- unique: true
- end
-
- create_table :motor_note_tag_tags do |t|
- t.references :tag, null: false, foreign_key: { to_table: :motor_note_tags }, index: true
- t.references :note, null: false, foreign_key: { to_table: :motor_notes }, index: false
-
- t.index %i[note_id tag_id],
- name: 'motor_note_tags_note_id_tag_id_index',
- unique: true
- end
-
- create_table :motor_reminders do |t|
- t.column :author_id, :bigint, null: false
- t.column :author_type, :string, null: false
- t.column :recipient_id, :bigint, null: false
- t.column :recipient_type, :string, null: false
- t.column :record_id, :string
- t.column :record_type, :string
- t.column :scheduled_at, :datetime, null: false, index: true
-
- t.timestamps
-
- t.index %i[author_id author_type],
- name: 'motor_reminders_author_id_author_type_index'
-
- t.index %i[recipient_id recipient_type],
- name: 'motor_reminders_recipient_id_recipient_type_index'
-
- t.index %i[record_id record_type],
- name: 'motor_reminders_record_id_record_type_index'
- end
-
- create_table :motor_notifications do |t|
- t.column :title, :string, null: false
- t.column :description, :text
- t.column :recipient_id, :bigint, null: false
- t.column :recipient_type, :string, null: false
- t.column :record_id, :string
- t.column :record_type, :string
- t.column :status, :string, null: false
-
- t.timestamps
-
- t.index %i[recipient_id recipient_type],
- name: 'motor_notifications_recipient_id_recipient_type_index'
-
- t.index %i[record_id record_type],
- name: 'motor_notifications_record_id_record_type_index'
- end
-
- add_index :motor_audits, %i[auditable_type auditable_id version], name: 'motor_auditable_index'
- add_index :motor_audits, %i[associated_type associated_id], name: 'motor_auditable_associated_index'
- add_index :motor_audits, %i[user_id user_type], name: 'motor_auditable_user_index'
- add_index :motor_audits, :request_uuid
- add_index :motor_audits, :created_at
-
- model = Class.new(ApplicationRecord)
-
- model.table_name = 'motor_configs'
-
- model.create!(key: 'header.links', value: [{
- name: '⭐ Star on GitHub',
- path: 'https://github.com/motor-admin/motor-admin-rails'
- }].to_json)
-
- model.table_name = 'motor_api_configs'
-
- model.create!(name: 'origin', url: '/', preferences: {}, credentials: {})
- end
-
- def self.down
- drop_table :motor_audits
- drop_table :motor_alert_locks
- drop_table :motor_alerts
- drop_table :motor_forms
- drop_table :motor_taggable_tags
- drop_table :motor_tags
- drop_table :motor_resources
- drop_table :motor_configs
- drop_table :motor_queries
- drop_table :motor_dashboards
- drop_table :motor_api_configs
- drop_table :motor_note_tag_tags
- drop_table :motor_note_tags
- drop_table :motor_notes
- drop_table :motor_notifications
- drop_table :motor_reminders
- end
-end
diff --git a/db/migrate/20240416055500_create_learning_materials.rb b/db/migrate/20240416055500_create_learning_materials.rb
new file mode 100644
index 00000000..2d2c8054
--- /dev/null
+++ b/db/migrate/20240416055500_create_learning_materials.rb
@@ -0,0 +1,14 @@
+class CreateLearningMaterials < ActiveRecord::Migration[7.0]
+ def change
+ create_table :learning_materials do |t|
+ t.references :user, null: false, foreign_key: true
+ t.string :thumbnail
+ t.integer :tag
+ t.string :learning_material_title
+ t.text :learning_material_description
+ t.string :learning_material_link
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 0f6705eb..d014ce41 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2023_05_31_154135) do
+ActiveRecord::Schema[7.0].define(version: 2024_04_16_055500) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -67,6 +67,18 @@
t.datetime "updated_at", null: false
end
+ create_table "learning_materials", force: :cascade do |t|
+ t.bigint "user_id", null: false
+ t.string "thumbnail"
+ t.integer "tag"
+ t.string "learning_material_title"
+ t.text "learning_material_description"
+ t.string "learning_material_link"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["user_id"], name: "index_learning_materials_on_user_id"
+ end
+
create_table "motor_alert_locks", force: :cascade do |t|
t.bigint "alert_id", null: false
t.string "lock_timestamp", null: false
@@ -313,6 +325,7 @@
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
+ add_foreign_key "learning_materials", "users"
add_foreign_key "motor_alert_locks", "motor_alerts", column: "alert_id"
add_foreign_key "motor_alerts", "motor_queries", column: "query_id"
add_foreign_key "motor_note_tag_tags", "motor_note_tags", column: "tag_id"
diff --git a/spec/features/learning_materials_spec.rb b/spec/features/learning_materials_spec.rb
new file mode 100644
index 00000000..5f6fc713
--- /dev/null
+++ b/spec/features/learning_materials_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.feature 'LearningMaterials', type: :feature do
+ before :each do
+ visit learning_materials_path
+ end
+
+ it 'Displays the two main page headers' do
+ visit learning_materials_path
+ expect(page).to have_content('Featured Materials')
+ expect(page).to have_content('Other Resources')
+ end
+
+ it 'Display default text if there are no learning materials found' do
+ @learningmaterials = []
+ expect(page).to have_content('There are no created learning materials yet')
+ end
+end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
new file mode 100644
index 00000000..c616c677
--- /dev/null
+++ b/spec/rails_helper.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+# This file is copied to spec/ when you run 'rails generate rspec:install'
+require 'spec_helper'
+ENV['RAILS_ENV'] ||= 'test'
+require_relative '../config/environment'
+# Prevent database truncation if the environment is production
+abort('The Rails environment is running in production mode!') if Rails.env.production?
+require 'rspec/rails'
+# Add additional requires below this line. Rails is not loaded until this point!
+
+# Requires supporting ruby files with custom matchers and macros, etc, in
+# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
+# run as spec files by default. This means that files in spec/support that end
+# in _spec.rb will both be required and run as specs, causing the specs to be
+# run twice. It is recommended that you do not name files matching this glob to
+# end with _spec.rb. You can configure this pattern with the --pattern
+# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
+#
+# The following line is provided for convenience purposes. It has the downside
+# of increasing the boot-up time by auto-requiring all files in the support
+# directory. Alternatively, in the individual `*_spec.rb` files, manually
+# require only the support files necessary.
+#
+# Rails.root.glob('spec/support/**/*.rb').sort.each { |f| require f }
+
+# Checks for pending migrations and applies them before tests are run.
+# If you are not using ActiveRecord, you can remove these lines.
+begin
+ ActiveRecord::Migration.maintain_test_schema!
+rescue ActiveRecord::PendingMigrationError => e
+ abort e.to_s.strip
+end
+RSpec.configure do |config|
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
+ config.fixture_path = Rails.root.join('spec/fixtures')
+
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
+ # examples within a transaction, remove the following line or assign false
+ # instead of true.
+ config.use_transactional_fixtures = true
+
+ # You can uncomment this line to turn off ActiveRecord support entirely.
+ # config.use_active_record = false
+
+ # RSpec Rails can automatically mix in different behaviours to your tests
+ # based on their file location, for example enabling you to call `get` and
+ # `post` in specs under `spec/controllers`.
+ #
+ # You can disable this behaviour by removing the line below, and instead
+ # explicitly tag your specs with their type, e.g.:
+ #
+ # RSpec.describe UsersController, type: :controller do
+ # # ...
+ # end
+ #
+ # The different available types are documented in the features, such as in
+ # https://rspec.info/features/6-0/rspec-rails
+ config.infer_spec_type_from_file_location!
+
+ # Filter lines from Rails gems in backtraces.
+ config.filter_rails_from_backtrace!
+ # arbitrary gems may also be filtered via:
+ # config.filter_gems_from_backtrace("gem name")
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 00000000..409c64b6
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+# This file was generated by the `rails generate rspec:install` command. Conventionally, all
+# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
+# The generated `.rspec` file contains `--require spec_helper` which will cause
+# this file to always be loaded, without a need to explicitly require it in any
+# files.
+#
+# Given that it is always loaded, you are encouraged to keep this file as
+# light-weight as possible. Requiring heavyweight dependencies from this file
+# will add to the boot time of your test suite on EVERY test run, even for an
+# individual file that may not need all of that loaded. Instead, consider making
+# a separate helper file that requires the additional dependencies and performs
+# the additional setup, and require it from the spec files that actually need
+# it.
+#
+# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # This option will default to `true` in RSpec 4. It makes the `description`
+ # and `failure_message` of custom matchers include text for helper methods
+ # defined using `chain`, e.g.:
+ # be_bigger_than(2).and_smaller_than(4).description
+ # # => "be bigger than 2 and smaller than 4"
+ # ...rather than:
+ # # => "be bigger than 2"
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended, and will default to
+ # `true` in RSpec 4.
+ mocks.verify_partial_doubles = true
+ end
+
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
+ # have no way to turn it off -- the option exists only for backwards
+ # compatibility in RSpec 3). It causes shared context metadata to be
+ # inherited by the metadata hash of host groups and examples, rather than
+ # triggering implicit auto-inclusion in groups with matching metadata.
+ config.shared_context_metadata_behavior = :apply_to_host_groups
+
+ # The settings below are suggested to provide a good initial experience
+ # with RSpec, but feel free to customize to your heart's content.
+ # # This allows you to limit a spec run to individual examples or groups
+ # # you care about by tagging them with `:focus` metadata. When nothing
+ # # is tagged with `:focus`, all examples get run. RSpec also provides
+ # # aliases for `it`, `describe`, and `context` that include `:focus`
+ # # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
+ # config.filter_run_when_matching :focus
+ #
+ # # Allows RSpec to persist some state between runs in order to support
+ # # the `--only-failures` and `--next-failure` CLI options. We recommend
+ # # you configure your source control system to ignore this file.
+ # config.example_status_persistence_file_path = "spec/examples.txt"
+ #
+ # # Limits the available syntax to the non-monkey patched syntax that is
+ # # recommended. For more details, see:
+ # # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/
+ # config.disable_monkey_patching!
+ #
+ # # Many RSpec users commonly either run the entire suite or an individual
+ # # file, and it's useful to allow more verbose output when running an
+ # # individual spec file.
+ # if config.files_to_run.one?
+ # # Use the documentation formatter for detailed output,
+ # # unless a formatter has already been configured
+ # # (e.g. via a command-line flag).
+ # config.default_formatter = "doc"
+ # end
+ #
+ # # Print the 10 slowest examples and example groups at the
+ # # end of the spec run, to help surface which specs are running
+ # # particularly slow.
+ # config.profile_examples = 10
+ #
+ # # Run specs in random order to surface order dependencies. If you find an
+ # # order dependency and want to debug it, you can fix the order by providing
+ # # the seed, which is printed after each run.
+ # # --seed 1234
+ # config.order = :random
+ #
+ # # Seed global randomization in this process using the `--seed` CLI option.
+ # # Setting this allows you to use `--seed` to deterministically reproduce
+ # # test failures related to randomization by passing the same `--seed` value
+ # # as the one that triggered the failure.
+ # Kernel.srand config.seed
+end
diff --git a/test/controllers/learning_materials_controller_test.rb b/test/controllers/learning_materials_controller_test.rb
new file mode 100644
index 00000000..037865c8
--- /dev/null
+++ b/test/controllers/learning_materials_controller_test.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class LearningMaterialsControllerTest < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/fixtures/learning_materials.yml b/test/fixtures/learning_materials.yml
new file mode 100644
index 00000000..15fcfff2
--- /dev/null
+++ b/test/fixtures/learning_materials.yml
@@ -0,0 +1,17 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ user: one
+ thumbnail: MyString
+ tag: 1
+ learning_material_title: MyString
+ learning_material_description: MyText
+ learning_material_link: MyString
+
+two:
+ user: two
+ thumbnail: MyString
+ tag: 1
+ learning_material_title: MyString
+ learning_material_description: MyText
+ learning_material_link: MyString
diff --git a/test/models/learning_material_test.rb b/test/models/learning_material_test.rb
new file mode 100644
index 00000000..84352f52
--- /dev/null
+++ b/test/models/learning_material_test.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class LearningMaterialTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end