Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/controllers/student_homes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,15 @@ class StudentHomesController < ApplicationController

def index
@student = Current.student
classroom = @student.classroom
@classroom_programs = classroom.classroom_programs
.includes(:program)
.order("programs.name")
@active_program = @classroom_programs.find { |cp| cp.id.to_s == params[:classroom_program_id] } || @classroom_programs.first
@published_modules = if @active_program
@active_program.classroom_modules.published.includes(content_module: :links)
else
[]
end
end
end
55 changes: 46 additions & 9 deletions app/views/student_homes/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
<section class="py-10 flex items-center justify-center">
<div class="card card-border bg-base-100 shadow-md w-96">
<div class="card-body">
<h1 class="card-title">Welcome to EndsideOut!</h1>
<p>You are logged in as <strong><%= @student.full_name %></strong></p>
<div class="card-actions justify-end">
<%= button_to "Logout", student_session_path, method: :delete, class: "btn btn-primary" %>
</div>
<% content_for :title, "Home" %>

<section class="py-10">
<header class="flex justify-between items-center mb-6">
<h1 class="text-2xl">Welcome, <%= @student.first_name %>!</h1>
<%= button_to "Logout", student_session_path, method: :delete, class: "btn btn-sm btn-ghost" %>
</header>

<% if @classroom_programs.size > 1 %>
<div role="tablist" class="tabs tabs-border mb-6">
<% @classroom_programs.each do |enrollment| %>
<%= link_to student_homes_path(classroom_program_id: enrollment.id),
role: "tab",
aria: { selected: enrollment == @active_program },
class: "tab #{"tab-active" if enrollment == @active_program}" do %>
<%= enrollment.program.name %>
<% end %>
<% end %>
</div>
</div>
<% end %>

<% sorted = @published_modules.sort_by { |m| m.content_module.position.to_i }
latest_date = sorted.map(&:publish_on).max %>
<% if sorted.any? %>
<% sorted.each do |classroom_module| %>
<details class="collapse collapse-arrow bg-base-100 border border-base-300 mb-2"
<%= "open" if classroom_module.publish_on == latest_date %>>
<summary class="collapse-title font-semibold">
<%= classroom_module.content_module.name %>
</summary>
<div class="collapse-content">
<ul class="flex flex-col gap-2 pt-2">
<% classroom_module.content_module.links.each do |link| %>
<li>
<a href="<%= safe_external_url(link.url) %>" target="_blank" rel="noopener noreferrer"
class="link inline-flex items-center gap-1">
<%= link.title %> ↗
</a>
</li>
<% end %>
</ul>
</div>
</details>
<% end %>
<% else %>
<p class="text-sm opacity-60">No modules published yet.</p>
<% end %>
</section>
1 change: 1 addition & 0 deletions test/controllers/classrooms_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class ClassroomsControllerTest < ActionDispatch::IntegrationTest

test "should update an existing enrollment level" do
enrollment = classroom_programs(:one)
enrollment.classroom_modules.update_all(publish_on: nil)

patch classroom_url(@classroom), params: {
classroom: {
Expand Down
82 changes: 82 additions & 0 deletions test/controllers/student_homes_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
require "test_helper"

class StudentHomesControllerTest < ActionDispatch::IntegrationTest
setup do
@student = students(:ada)
student_sign_in_as @student
end

test "unauthenticated request redirects to student login" do
student_sign_out
get student_homes_url
assert_redirected_to new_student_session_url
end

test "shows published module names" do
get student_homes_url
assert_response :success
assert_select "summary", text: /Introduction to Health/
end

test "shows links inside published modules" do
get student_homes_url
assert_response :success
link = content_modules(:intro).links.first
assert_select "a[target='_blank']", text: /#{link.title}/ if link
end

test "most recently published module has the open attribute" do
# Add a second published module so there's a distinct "most recent"
second_module = ContentModule.create!(
program: programs(:kyh), level: "basic", name: "Second Module", position: 2
)
classroom_programs(:one).classroom_modules.create!(
content_module: second_module, publish_on: Date.current
)

get student_homes_url
assert_select "details[open] summary", text: /Second Module/
end

test "all modules published on the same latest date have the open attribute" do
second_module = ContentModule.create!(
program: programs(:kyh), level: "basic", name: "Second Module", position: 2
)
classroom_modules(:one).update!(publish_on: Date.current)
classroom_programs(:one).classroom_modules.create!(
content_module: second_module, publish_on: Date.current
)

get student_homes_url
assert_select "details[open]", count: 2
end

test "shows empty state when no modules are published" do
classroom_modules(:one).update!(publish_on: nil)
get student_homes_url
assert_select "p", text: /No modules published yet/
end

test "does not show tab bar for single program enrollment" do
get student_homes_url
assert_select "[role='tablist']", count: 0
end

test "shows tab bar when classroom has multiple program enrollments" do
classroom_programs(:one).classroom.classroom_programs.create!(
program: programs(:"3dw"), level: "moderate"
)

get student_homes_url
assert_select "[role='tablist']"
assert_select "a[role='tab']", minimum: 2
end

test "tab switching shows the selected program as active" do
enrollment = classroom_programs(:one)
enrollment.classroom.classroom_programs.create!(program: programs(:"3dw"), level: "moderate")

get student_homes_url(classroom_program_id: enrollment.id)
assert_select "a[role='tab'][aria-selected='true'][href*='classroom_program_id=#{enrollment.id}']"
end
end
1 change: 1 addition & 0 deletions test/fixtures/classroom_modules.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
one:
classroom_program: one
content_module: intro
publish_on: 2026-05-01

scheduled:
classroom_program: two
Expand Down
15 changes: 8 additions & 7 deletions test/models/classroom_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,26 @@ class ClassroomTest < ActiveSupport::TestCase

test "updates level via nested attributes" do
classroom = classrooms(:one)
cp = classroom_programs(:one)
enrollment = classroom_programs(:one)
enrollment.classroom_modules.update_all(publish_on: nil)

classroom.update!(
classroom_programs_attributes: [ { id: cp.id, program_id: cp.program_id, level: "advanced" } ]
classroom_programs_attributes: [ { id: enrollment.id, program_id: enrollment.program_id, level: "advanced" } ]
)

assert_equal "advanced", cp.reload.level
assert_equal "advanced", enrollment.reload.level
end

test "destroys enrollment via nested attributes" do
classroom = classrooms(:one)
cp = classroom_programs(:one)
enrollment = classroom_programs(:one)

# Add a second enrollment first so the classroom still has one after destruction
classroom.classroom_programs.create!(program: programs(:"3dw"), level: "basic")

assert_difference "ClassroomProgram.count", -1 do
classroom.update!(
classroom_programs_attributes: [ { id: cp.id, _destroy: "1" } ]
classroom_programs_attributes: [ { id: enrollment.id, _destroy: "1" } ]
)
end
end
Expand Down Expand Up @@ -64,10 +65,10 @@ class ClassroomTest < ActiveSupport::TestCase

test "is invalid when all programs are removed" do
classroom = classrooms(:one)
cp = classroom_programs(:one)
enrollment = classroom_programs(:one)

classroom.assign_attributes(
classroom_programs_attributes: [ { id: cp.id, _destroy: "1" } ]
classroom_programs_attributes: [ { id: enrollment.id, _destroy: "1" } ]
)

assert_not classroom.valid?
Expand Down