diff --git a/app/components/app_session_table_component.html.erb b/app/components/app_session_table_component.html.erb
new file mode 100644
index 000000000..b74924519
--- /dev/null
+++ b/app/components/app_session_table_component.html.erb
@@ -0,0 +1,77 @@
+
+
<%= heading %>
+
+ <%= govuk_table(html_attributes: { class: "nhsuk-table-responsive" }) do |table| %>
+ <% table.with_head do |head| %>
+ <% head.with_row do |row| %>
+ <% row.with_cell(text: "Location") %>
+ <% row.with_cell(text: "Dates") %>
+ <% row.with_cell(text: "Programmes") if show_programmes %>
+ <% row.with_cell(text: "Consent period") if show_consent_period %>
+ <% row.with_cell(text: "Children", numeric: true) %>
+ <% end %>
+ <% end %>
+
+ <% table.with_body do |body| %>
+ <% sessions.each do |session| %>
+ <% body.with_row do |row| %>
+ <% row.with_cell do %>
+
Location
+
+
+ <%= govuk_link_to session.location.name, session_path(session) %>
+
+ <% if (location = session.location).has_address? %>
+
+
+ <%= helpers.format_address_single_line(location) %>
+
+ <% end %>
+
+ <% end %>
+
+ <% row.with_cell do %>
+
Dates
+
+
+ <% session.dates.each do |date| %>
+ - <%= date.value.to_fs(:long) %>
+ <% end %>
+
+ <% end %>
+
+ <% if show_programmes %>
+ <% row.with_cell do %>
+
Programmes
+
+
+ <% session.programmes.each do |programme| %>
+ - <%= programme.name %>
+ <% end %>
+
+ <% end %>
+ <% end %>
+
+ <% if show_consent_period %>
+ <% row.with_cell do %>
+
Consent period
+
+ <% if session.close_consent_at.nil? %>
+ n/a
+ <% elsif session.close_consent_at.past? %>
+ <%= "Closed #{session.close_consent_at.to_fs(:short)}" %>
+ <% else %>
+ <%= "Open until #{session.close_consent_at.to_fs(:short)}" %>
+ <% end %>
+ <% end %>
+ <% end %>
+
+ <% row.with_cell(numeric: true) do %>
+
Children
+ <%= (count = session.patients.count).zero? ? "None" : count %>
+ <% end %>
+ <% end %>
+ <% end %>
+ <% end %>
+ <% end %>
+
diff --git a/app/components/app_session_table_component.rb b/app/components/app_session_table_component.rb
new file mode 100644
index 000000000..84a73cfc3
--- /dev/null
+++ b/app/components/app_session_table_component.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AppSessionTableComponent < ViewComponent::Base
+ def initialize(
+ sessions,
+ description: "sessions",
+ show_programmes: false,
+ show_consent_period: false
+ )
+ super
+
+ @sessions = sessions
+ @description = description
+ @show_programmes = show_programmes
+ @show_consent_period = show_consent_period
+ end
+
+ private
+
+ attr_reader :sessions, :show_programmes, :show_consent_period
+
+ def heading
+ pluralize(sessions.count, @description)
+ end
+end
diff --git a/spec/components/app_session_table_component_spec.rb b/spec/components/app_session_table_component_spec.rb
new file mode 100644
index 000000000..45b1ef62c
--- /dev/null
+++ b/spec/components/app_session_table_component_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+describe AppSessionTableComponent do
+ subject(:rendered) { render_inline(component) }
+
+ let(:component) { described_class.new(sessions) }
+
+ let(:sessions) do
+ [
+ create(
+ :session,
+ academic_year: 2024,
+ date: Date.new(2024, 10, 1),
+ location: create(:location, :school, name: "Waterloo Road"),
+ programme: create(:programme, :hpv)
+ )
+ ] + create_list(:session, 9)
+ end
+
+ before { create_list(:patient, 5, session: sessions.first) }
+
+ it { should have_css(".nhsuk-table__heading-tab", text: "10 sessions") }
+
+ context "with a custom description" do
+ let(:component) do
+ described_class.new(sessions, description: "active sessions")
+ end
+
+ it { should have_content("10 active sessions") }
+ end
+
+ it "renders the headers" do
+ expect(rendered).to have_css(".nhsuk-table__header", text: "Location")
+ expect(rendered).to have_css(".nhsuk-table__header", text: "Dates")
+ expect(rendered).to have_css(".nhsuk-table__header", text: "Children")
+ end
+
+ it "renders the rows" do
+ expect(rendered).to have_css(
+ ".nhsuk-table__body .nhsuk-table__row",
+ count: 10
+ )
+
+ expect(rendered).to have_css(".nhsuk-table__cell", text: "Waterloo Road")
+
+ expect(rendered).to have_css(".nhsuk-table__cell", text: "1 October 2024")
+
+ expect(rendered).to have_css(".nhsuk-table__cell", text: "5")
+ expect(rendered).to have_css(".nhsuk-table__cell", text: "None")
+ end
+
+ context "when showing programmes" do
+ let(:component) { described_class.new(sessions, show_programmes: true) }
+
+ it { should have_css(".nhsuk-table__header", text: "Programmes") }
+ it { should have_css(".nhsuk-table__cell", text: "HPV") }
+ end
+
+ context "when showing consent period" do
+ let(:component) { described_class.new(sessions, show_consent_period: true) }
+
+ it { should have_css(".nhsuk-table__header", text: "Consent period") }
+ it { should have_css(".nhsuk-table__cell", text: "Open until 1 October") }
+ end
+end