Skip to content

Commit bac49ee

Browse files
committed
Merge branch 'main' into 29-epic-5-file-storage-training-resources
2 parents a174569 + a846218 commit bac49ee

File tree

13 files changed

+332
-137
lines changed

13 files changed

+332
-137
lines changed

Gemfile.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ GEM
158158
actionview (>= 5.0.0)
159159
activesupport (>= 5.0.0)
160160
json (2.9.1)
161-
kamal (2.5.0)
161+
kamal (2.5.2)
162162
activesupport (>= 7.0)
163163
base64 (~> 0.2)
164164
bcrypt_pbkdf (~> 1.0)
@@ -246,7 +246,7 @@ GEM
246246
nio4r (~> 2.0)
247247
raabro (1.4.0)
248248
racc (1.8.1)
249-
rack (3.1.8)
249+
rack (3.1.9)
250250
rack-session (2.1.0)
251251
base64 (>= 0.1.0)
252252
rack (>= 3.0.0)
@@ -292,21 +292,21 @@ GEM
292292
rb-fsevent (0.11.2)
293293
rb-inotify (0.11.1)
294294
ffi (~> 1.0)
295-
rdoc (6.11.0)
295+
rdoc (6.12.0)
296296
psych (>= 4.0.0)
297297
regexp_parser (2.10.0)
298298
reline (0.6.0)
299299
io-console (~> 0.5)
300300
rexml (3.4.0)
301-
rspec-core (3.13.2)
301+
rspec-core (3.13.3)
302302
rspec-support (~> 3.13.0)
303303
rspec-expectations (3.13.3)
304304
diff-lcs (>= 1.2.0, < 2.0)
305305
rspec-support (~> 3.13.0)
306306
rspec-mocks (3.13.2)
307307
diff-lcs (>= 1.2.0, < 2.0)
308308
rspec-support (~> 3.13.0)
309-
rspec-rails (7.1.0)
309+
rspec-rails (7.1.1)
310310
actionpack (>= 7.0)
311311
activesupport (>= 7.0)
312312
railties (>= 7.0)
@@ -362,7 +362,7 @@ GEM
362362
activejob (>= 7.2)
363363
activerecord (>= 7.2)
364364
railties (>= 7.2)
365-
solid_cache (1.0.6)
365+
solid_cache (1.0.7)
366366
activejob (>= 7.2)
367367
activerecord (>= 7.2)
368368
railties (>= 7.2)

app/controllers/topics_controller.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ class TopicsController < ApplicationController
22
before_action :set_topic, only: [ :show, :edit, :update, :destroy, :archive ]
33

44
def index
5-
@topics = scope.includes(:language, :provider)
5+
@topics = scope.search_with_params(search_params)
6+
@providers = scope.map(&:provider).uniq.sort_by(&:name)
7+
@languages = scope.map(&:language).uniq.sort_by(&:name)
68
end
79

810
def new
@@ -47,6 +49,13 @@ def topic_params
4749
params.require(:topic).permit(:title, :description, :uid, :language_id, :provider_id)
4850
end
4951

52+
helper_method :search_params
53+
def search_params
54+
return {} unless params[:search].present?
55+
56+
params.require(:search).permit(:query, :state, :provider_id, :language_id, :year, :month, :order)
57+
end
58+
5059
def set_topic
5160
@topic = Topic.find(params[:id])
5261
end
@@ -55,7 +64,7 @@ def scope
5564
@scope ||= if Current.user.is_admin?
5665
Topic.all
5766
else
58-
Topic.active
59-
end
67+
Current.user.topics
68+
end.includes(:language, :provider)
6069
end
6170
end

app/models/concerns/searcheable.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module Searcheable
2+
extend ActiveSupport::Concern
3+
4+
SORTS = %i[asc desc].freeze
5+
6+
class_methods do
7+
def search(query)
8+
where("title ILIKE :query OR description ILIKE :query", query: "%#{query}%")
9+
end
10+
11+
def by_year(year)
12+
where("extract(year from created_at) = ?", year)
13+
end
14+
15+
def by_month(month)
16+
where("extract(month from created_at) = ?", month)
17+
end
18+
19+
def by_provider(provider_id)
20+
where(provider_id: provider_id)
21+
end
22+
23+
def by_language(language_id)
24+
where(language_id: language_id)
25+
end
26+
27+
def by_state(state)
28+
where(state: state)
29+
end
30+
31+
def sort_order(order_from_params)
32+
return :desc unless SORTS.include?(order_from_params)
33+
34+
order_from_params
35+
end
36+
37+
def search_with_params(params)
38+
self
39+
.then { |scope| params[:state].present? ? scope.by_state(params[:state]) : scope }
40+
.then { |scope| params[:provider_id].present? ? scope.by_provider(params[:provider_id]) : scope }
41+
.then { |scope| params[:language_id].present? ? scope.by_language(params[:language_id]): scope }
42+
.then { |scope| params[:year].present? ? scope.by_year(params[:year]) : scope }
43+
.then { |scope| params[:month].present? ? scope.by_month(params[:month]) : scope }
44+
.then { |scope| params[:query].present? ? scope.search(params[:query]) : scope }
45+
.then { |scope| scope.order(created_at: sort_order(params[:order])) }
46+
end
47+
end
48+
end

app/models/topic.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
class Topic < ApplicationRecord
2+
include Searcheable
3+
24
belongs_to :language
35
belongs_to :provider
46
has_many :training_resources

app/models/user.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@ class User < ApplicationRecord
88

99
validates :email, presence: true, uniqueness: true, format: URI::MailTo::EMAIL_REGEXP
1010
validates :password_digest, presence: true
11+
12+
def topics
13+
Topic.where(provider_id: providers.pluck(:id))
14+
end
1115
end

app/views/home/index.html.erb

Lines changed: 121 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,123 +1,128 @@
1-
<body class="d-flex flex-column min-vh-100">
2-
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
3-
<div class="container">
4-
<a class="navbar-brand fw-bold" href="#">SkillRx</a>
5-
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
6-
<span class="navbar-toggler-icon"></span>
7-
</button>
8-
<div class="collapse navbar-collapse" id="navbarNav">
9-
<ul class="navbar-nav ms-auto">
10-
<li class="nav-item"><a class="nav-link" href="#about">About</a></li>
11-
<li class="nav-item"><a class="nav-link" href="#features">Features</a></li>
12-
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
13-
<!-- New Sign In link using Rails 8 authentication -->
14-
<li class="nav-item">
15-
<%= link_to "Sign In", new_session_path, class: "nav-link" %>
16-
</li>
17-
</ul>
18-
</div>
19-
</div>
20-
</nav>
1+
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
2+
<div class="container">
3+
<a class="navbar-brand fw-bold" href="#">SkillRx</a>
4+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
5+
<span class="navbar-toggler-icon"></span>
6+
</button>
7+
<div class="collapse navbar-collapse" id="navbarNav">
8+
<ul class="navbar-nav ms-auto">
9+
<li class="nav-item"><a class="nav-link" href="#about">About</a></li>
10+
<li class="nav-item"><a class="nav-link" href="#features">Features</a></li>
11+
<li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li>
12+
<!-- New Sign In link using Rails 8 authentication -->
13+
<% if authenticated? %>
14+
<li class="nav-item">
15+
<%= link_to "Sign Out", session_path, method: :delete, class: "nav-link" %>
16+
</li>
17+
<% else %>
18+
<li class="nav-item">
19+
<%= link_to "Sign In", new_session_path, class: "nav-link" %>
20+
</li>
21+
<% end %>
22+
</ul>
23+
</div>
24+
</div>
25+
</nav>
2126

22-
<!-- Hero Section -->
23-
<header class="bg-primary text-white py-5">
24-
<div class="container">
25-
<div class="row align-items-center">
26-
<div class="col-lg-6">
27-
<h1 class="display-4 fw-bold">Medical Education Without Borders</h1>
28-
<p class="lead">Empowering healthcare professionals worldwide with accessible continuing medical education</p>
29-
<%= link_to "Get Started", new_session_path, class: "btn btn-light btn-lg" %>
30-
</div>
31-
<div class="col-lg-6">
32-
<img src="/api/placeholder/600/400" alt="Medical professionals using SkillRx" class="img-fluid rounded">
33-
</div>
34-
</div>
35-
</div>
36-
</header>
27+
<!-- Hero Section -->
28+
<header class="bg-primary text-white py-5">
29+
<div class="container">
30+
<div class="row align-items-center">
31+
<div class="col-lg-6">
32+
<h1 class="display-4 fw-bold">Medical Education Without Borders</h1>
33+
<p class="lead">Empowering healthcare professionals worldwide with accessible continuing medical education</p>
34+
<%= link_to "Get Started", new_session_path, class: "btn btn-light btn-lg" %>
35+
</div>
36+
<div class="col-lg-6">
37+
<img src="/api/placeholder/600/400" alt="Medical professionals using SkillRx" class="img-fluid rounded">
38+
</div>
39+
</div>
40+
</div>
41+
</header>
3742

38-
<!-- Features Section -->
39-
<section id="features" class="py-5">
40-
<div class="container">
41-
<h2 class="text-center mb-5">How SkillRx Works</h2>
42-
<div class="row g-4">
43-
<div class="col-md-4">
44-
<div class="card h-100">
45-
<div class="card-body">
46-
<h3 class="card-title h5">Offline Access</h3>
47-
<p class="card-text">Access medical education content anytime, anywhere - no internet required</p>
48-
</div>
49-
</div>
50-
</div>
51-
<div class="col-md-4">
52-
<div class="card h-100">
53-
<div class="card-body">
54-
<h3 class="card-title h5">Smart Sync</h3>
55-
<p class="card-text">Automatically update content when connected to optimize your learning</p>
56-
</div>
57-
</div>
58-
</div>
59-
<div class="col-md-4">
60-
<div class="card h-100">
61-
<div class="card-body">
62-
<h3 class="card-title h5">Multi-Platform</h3>
63-
<p class="card-text">Available via USB drive, mobile app, or local server for maximum flexibility</p>
64-
</div>
65-
</div>
66-
</div>
67-
</div>
43+
<!-- Features Section -->
44+
<section id="features" class="py-5">
45+
<div class="container">
46+
<h2 class="text-center mb-5">How SkillRx Works</h2>
47+
<div class="row g-4">
48+
<div class="col-md-4">
49+
<div class="card h-100">
50+
<div class="card-body">
51+
<h3 class="card-title h5">Offline Access</h3>
52+
<p class="card-text">Access medical education content anytime, anywhere - no internet required</p>
53+
</div>
6854
</div>
69-
</section>
70-
71-
<!-- About Section -->
72-
<section id="about" class="bg-light py-5">
73-
<div class="container">
74-
<div class="row align-items-center">
75-
<div class="col-lg-6">
76-
<h2>Our Mission</h2>
77-
<p class="lead">Enabling medical practitioners in resource-constrained areas to maintain competence and improve patient care through accessible continuing education.</p>
78-
<ul class="list-unstyled">
79-
<li class="mb-2">✓ Free and open access</li>
80-
<li class="mb-2">✓ Cutting-edge medical literature</li>
81-
<li class="mb-2">✓ Adaptable to local needs</li>
82-
</ul>
83-
</div>
84-
<div class="col-lg-6">
85-
<img src="/api/placeholder/600/400" alt="Healthcare professionals collaborating" class="img-fluid rounded">
86-
</div>
87-
</div>
55+
</div>
56+
<div class="col-md-4">
57+
<div class="card h-100">
58+
<div class="card-body">
59+
<h3 class="card-title h5">Smart Sync</h3>
60+
<p class="card-text">Automatically update content when connected to optimize your learning</p>
61+
</div>
62+
</div>
63+
</div>
64+
<div class="col-md-4">
65+
<div class="card h-100">
66+
<div class="card-body">
67+
<h3 class="card-title h5">Multi-Platform</h3>
68+
<p class="card-text">Available via USB drive, mobile app, or local server for maximum flexibility</p>
69+
</div>
8870
</div>
89-
</section>
71+
</div>
72+
</div>
73+
</div>
74+
</section>
9075

91-
<!-- Contact Section -->
92-
<section id="contact" class="py-5">
93-
<div class="container">
94-
<div class="row justify-content-center">
95-
<div class="col-lg-6 text-center">
96-
<h2>Get In Touch</h2>
97-
<p class="lead mb-4">Questions? We're here to help.</p>
98-
<div class="d-grid gap-2">
99-
<a href="mailto:[email protected]" class="btn btn-primary">Email Us</a>
100-
<a href="#survey" class="btn btn-outline-primary">Take Our Survey</a>
101-
</div>
102-
</div>
103-
</div>
76+
<!-- About Section -->
77+
<section id="about" class="bg-light py-5">
78+
<div class="container">
79+
<div class="row align-items-center">
80+
<div class="col-lg-6">
81+
<h2>Our Mission</h2>
82+
<p class="lead">Enabling medical practitioners in resource-constrained areas to maintain competence and improve patient care through accessible continuing education.</p>
83+
<ul class="list-unstyled">
84+
<li class="mb-2">✓ Free and open access</li>
85+
<li class="mb-2">✓ Cutting-edge medical literature</li>
86+
<li class="mb-2">✓ Adaptable to local needs</li>
87+
</ul>
88+
</div>
89+
<div class="col-lg-6">
90+
<img src="/api/placeholder/600/400" alt="Healthcare professionals collaborating" class="img-fluid rounded">
91+
</div>
92+
</div>
93+
</div>
94+
</section>
95+
96+
<!-- Contact Section -->
97+
<section id="contact" class="py-5">
98+
<div class="container">
99+
<div class="row justify-content-center">
100+
<div class="col-lg-6 text-center">
101+
<h2>Get In Touch</h2>
102+
<p class="lead mb-4">Questions? We're here to help.</p>
103+
<div class="d-grid gap-2">
104+
<a href="mailto:[email protected]" class="btn btn-primary">Email Us</a>
105+
<a href="#survey" class="btn btn-outline-primary">Take Our Survey</a>
104106
</div>
105-
</section>
107+
</div>
108+
</div>
109+
</div>
110+
</section>
106111

107-
<!-- Ensure main content takes available space -->
108-
<main class="flex-fill">
109-
<!-- Footer anchored at the bottom -->
110-
<footer class="bg-dark text-white py-4 mt-auto">
111-
<div class="container">
112-
<div class="row">
113-
<div class="col-md-6">
114-
<p class="mb-0">© 2025 SkillRx</p>
115-
</div>
116-
<div class="col-md-6 text-md-end">
117-
<a href="#privacy" class="text-white me-3">Privacy Policy</a>
118-
<a href="#terms" class="text-white">Terms of Use</a>
119-
</div>
120-
</div>
121-
</div>
122-
</footer>
123-
</main>
112+
<!-- Ensure main content takes available space -->
113+
<main class="flex-fill">
114+
<!-- Footer anchored at the bottom -->
115+
<footer class="bg-dark text-white py-4 mt-auto">
116+
<div class="container">
117+
<div class="row">
118+
<div class="col-md-6">
119+
<p class="mb-0">© 2025 SkillRx</p>
120+
</div>
121+
<div class="col-md-6 text-md-end">
122+
<a href="#privacy" class="text-white me-3">Privacy Policy</a>
123+
<a href="#terms" class="text-white">Terms of Use</a>
124+
</div>
125+
</div>
126+
</div>
127+
</footer>
128+
</main>

0 commit comments

Comments
 (0)