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
4 changes: 2 additions & 2 deletions app/components/projects/log_schema_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<%= render BadgeComponent.new(text: t(".attributes_count", count: project.log_schema.size), colour: :warning, size: :sm) %>
</div>
<div>
<%= render ActionButtonComponent.new(to: edit_project_attributes_schema_path(project), icon: "edit", colour: :info, turbo_stream: true, size: :medium) do %>
<%= render ActionButtonComponent.new(to: edit_project_attributes_schema_path(project), icon: "edit", colour: :primary, turbo_stream: true, size: :medium) do %>
<%= t(".edit_attributes") %>
<% end %>
</div>
Expand All @@ -24,7 +24,7 @@
<%= name %>
</span>
</div>
<%= render BadgeComponent.new(text: type.to_s, size: :xs, colour: :info, html_classes: "min-w-[65px] text-base-100") %>
<%= render BadgeComponent.new(text: type.to_s, size: :xs, colour: :primary, html_classes: "min-w-[65px] text-base-100") %>
</div>
<% end %>
</div>
Expand Down
41 changes: 25 additions & 16 deletions app/components/shared/index_table_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<div data-controller="index-table-component" data-index-table-component-per-page-value="<%= per_page %>">
<% if searchable %>
<div class="mb-4">
<label class="input input-bordered w-full">
<%= render IconComponent.new(icon: "search", size: :sm, classes: "h-[1em] opacity-50") %>
<input
type="search"
class="grow focus:outline-none"
placeholder="Search..."
autocomplete="off"
data-index-table-component-target="search"
data-action="input->index-table-component#filter">
<label class="group ui-enter-item input input-bordered w-full focus-within:border-primary focus-within:outline-none"
style="--ui-enter-delay: 40ms;">
<span class="text-base-content/50 group-focus-within:text-primary transition-colors">
<%= render IconComponent.new(icon: "search", size: :sm) %>
</span>

<input type="search" name="q"
placeholder="<%= t(".search_placeholder") %>"
class="grow"
autocomplete="off"
data-index-table-component-target="search"
data-action="input->index-table-component#filter">
</label>
</div>
<% end %>
Expand All @@ -19,26 +22,32 @@

<!-- Each row (including header) is its own rounded box -->
<div class="mb-2">
<div class="card bg-neutral rounded-lg">
<div class="p-4 grid items-center gap-4" style="grid-template-columns: <%= column_sizes %>">
<div class="card ui-enter-item bg-neutral rounded-lg" style="--ui-enter-delay: 80ms;">
<div class="p-4 grid items-stretch gap-4" style="grid-template-columns: <%= column_sizes %>">
<% columns.each do |column| %>
<div class="text-sm font-semibold text-neutral-content"><%= column.header %></div>
<div class="text-sm font-semibold text-neutral-content flex items-center"><%= column.header %></div>
<% end %>
</div>
</div>
</div>

<div class="space-y-2">
<% records.each_with_index do |record, index| %>
<div data-index-table-component-target="row" class="card bg-white rounded-lg shadow-sm">
<div class="p-3 grid items-center gap-4" style="grid-template-columns: <%= column_sizes %>">
<div
data-index-table-component-target="row"
class="card ui-enter-item bg-white rounded-lg group transition-shadow relative overflow-visible shadow-sm<%= " cursor-pointer hover:shadow-md" if row_clickable?(record) %>"
style="--ui-enter-delay: <%= 120 + ((index % 15) * 35) %>ms;">
<%= row_overlay_link(record) %>
<div class="p-3 grid items-stretch gap-4 rounded-lg<%= " group-hover:bg-secondary/30 transition-colors" if row_clickable?(record) %>" style="grid-template-columns: <%= column_sizes %>">
<% columns.each do |column| %>
<div class="text-sm text-base-content"><%= render_cell_content(record, column) %></div>
<div class="text-sm text-base-content min-w-0 w-full h-full flex items-center <%= "relative z-20" if column.attribute == :actions %>">
<%= render_cell_content(record, column) %>
</div>
<% end %>
</div>
</div>
<% end %>
<div data-index-table-component-target="emptyRow" class="card bg-white rounded-lg shadow-sm <%= "hidden" unless empty? %>">
<div data-index-table-component-target="emptyRow" class="card ui-enter-item bg-white rounded-lg shadow-sm <%= "hidden" unless empty? %>" style="--ui-enter-delay: 120ms;">
<div class="flex justify-center p-3 items-center">
<div class="text-sm text-base-content/40 font-bold"><%= t("shared.index_table_component.no_entries") %></div>
</div>
Expand Down
47 changes: 45 additions & 2 deletions app/components/shared/index_table_component.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
module Shared
class IndexTableComponent < ViewComponent::Base
attr_reader :records, :columns, :searchable, :per_page
attr_reader :records, :columns, :searchable, :per_page, :clickable_rows, :turbo_stream_rows

Column = Struct.new(:attribute, :header, :cell_renderer, :col_size)

def initialize(records:, searchable: true, per_page: 10)
def initialize(records:, searchable: true, per_page: 10, clickable_rows: true,
turbo_stream_rows: false, record_path: nil)
super()
@records = records
@columns = []
@searchable = searchable
@per_page = per_page
@clickable_rows = clickable_rows
@turbo_stream_rows = turbo_stream_rows
@record_path = record_path
end

def column(attribute, header: nil, col_size: nil, &cell_renderer)
Expand All @@ -19,6 +23,45 @@ def column(attribute, header: nil, col_size: nil, &cell_renderer)

private

def turbo_stream_url_for(path)
uri = URI.parse(path.to_s)
params = Rack::Utils.parse_nested_query(uri.query).merge("format" => "turbo_stream")

uri.query = params.to_query.presence

uri.to_s
end

def row_path_for(record)
return nil unless @record_path

path = @record_path.call(record)

return path unless @turbo_stream_rows

turbo_stream_url_for(path)
end

def row_link_data
{ turbo_stream: (@turbo_stream_rows ? true : nil) }.compact
end

def row_clickable?(record)
clickable_rows && row_path_for(record).present?
end

def row_overlay_link(record)
return unless row_clickable?(record)

link_to(
row_path_for(record),
data: row_link_data,
class: "absolute inset-0 rounded-lg z-10"
) do
content_tag(:span)
end
end

def render_cell_content(record, column)
if column.cell_renderer
view_context.capture(record, &column.cell_renderer)
Expand Down
50 changes: 27 additions & 23 deletions app/views/projects/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</div>

<div class="flex flex-row gap-4">
<%= render ActionButtonComponent.new(to: edit_project_path(@project), icon: "edit", colour: :info, turbo_stream: true, size: :large) do %>
<%= render ActionButtonComponent.new(to: edit_project_path(@project), icon: "edit", colour: :secondary, turbo_stream: true, size: :large) do %>
<%= t(".actions.edit") %>
<% end %>

Expand All @@ -21,30 +21,34 @@
</div>
</div>

<%= render ContentCardComponent.new do %>
<h1 class="text-2xl font-bold"><%= t(".subprojects") %></h1>
<%= render Shared::IndexTableComponent.new(records: @project.subprojects) do |table| %>
<% table.column :name do |subproject| %>
<%= subproject.name %>
<% end %>
<% table.column :address do |subproject| %>
<%= subproject.address %>
<% end %>
<% table.column :region do |subproject| %>
<%= subproject.region.name %>
<% end %>
<% table.column :created_at do |subproject| %>
<%= l(subproject.created_at) %>
<% end %>
<% table.column :actions, col_size: "90px" do |subproject| %>
<div class="flex justify-end gap-2">
<%= render ActionButtonComponent.new(to: project_subproject_path(@project, subproject), icon: "view") %>
<%= render ActionButtonComponent.new(to: edit_project_subproject_path(@project, subproject), icon: "edit", colour: :info, turbo_stream: true) %>
<%= render ActionButtonComponent.new(to: project_subproject_path(@project, subproject), method: :delete, icon: "delete", colour: :error, confirm: t("subprojects.destroy.confirm")) %>
</div>
<div class="ui-enter-section" style="--ui-enter-delay: 110ms;">
<%= render ContentCardComponent.new do %>
<h1 class="text-2xl font-bold"><%= t(".subprojects") %></h1>
<%= render Shared::IndexTableComponent.new(
records: @project.subprojects,
record_path: ->(subproject) { project_subproject_path(@project, subproject) }
) do |table| %>
<% table.column :name do |subproject| %>
<%= subproject.name %>
<% end %>
<% table.column :address do |subproject| %>
<%= subproject.address %>
<% end %>
<% table.column :region do |subproject| %>
<%= subproject.region.name %>
<% end %>
<% table.column :created_at do |subproject| %>
<%= l(subproject.created_at) %>
<% end %>
<% table.column :actions, header: t("shared.index_table_component.actions.label"), col_size: "65px" do |subproject| %>
<div class="flex flex-row gap-2 items-center justify-center">
<%= render ActionButtonComponent.new(to: edit_project_subproject_path(@project, subproject), icon: "edit", turbo_stream: true, colour: :primary) %>
<%= render ActionButtonComponent.new(to: project_subproject_path(@project, subproject), method: :delete, icon: "delete", colour: :error, confirm: t("subprojects.destroy.confirm")) %>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
</div>

<%= render ContentCardComponent.new do %>
<%= render Projects::LogSchemaComponent.new(project: @project) %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/projects/subprojects/journals/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="flex items-center justify-between mb-5">
<h1 class="text-4xl font-bold"><%= @journal.title %></h1>
<div class="flex flex-row gap-4">
<%= render ActionButtonComponent.new(to: edit_project_subproject_journal_path(@project, @subproject, @journal), icon: "edit", colour: :info, size: :large) do %>
<%= render ActionButtonComponent.new(to: edit_project_subproject_journal_path(@project, @subproject, @journal), icon: "edit", colour: :primary, size: :large) do %>
<%= t(".actions.edit") %>
<% end %>
<%= render ActionButtonComponent.new(to: project_subproject_journal_path(@project, @subproject, @journal), method: :delete, icon: "delete", colour: :error, size: :large, confirm: t("projects.subprojects.journals.destroy.confirm")) do %>
Expand Down
31 changes: 17 additions & 14 deletions app/views/regions/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,23 @@
<% end %>
</div>
<%= render ContentCardComponent.new do %>
<%= render Shared::IndexTableComponent.new(records: @regions) do |table| %>
<% table.column :name %>
<% table.column :latitude do |region| %>
<%= "#{region.latitude.abs.round(4)}° #{region.latitude >= 0 ? 'N' : 'S'}" %>
<% end %>
<% table.column :longitude do |region| %>
<%= "#{region.longitude.abs.round(4)}° #{region.longitude >= 0 ? 'E' : 'W'}" %>
<% end %>
<% table.column :actions, header: t("shared.index_table_component.actions"), col_size: "67px" do |region| %>
<div class="flex items-center gap-2">
<%= render ActionButtonComponent.new(to: edit_region_path(region), icon: "edit", colour: :info, turbo_stream: true) %>
<%= render ActionButtonComponent.new(to: region_path(region), method: :delete, icon: "delete", colour: :error, confirm: t("regions.destroy.confirm")) %>
</div>
<% end %>
<%= render Shared::IndexTableComponent.new(
records: @regions,
clickable_rows: false
) do |table| %>
<% table.column :name %>
<% table.column :latitude do |region| %>
<%= "#{region.latitude.abs.round(4)}° #{region.latitude >= 0 ? 'N' : 'S'}" %>
<% end %>
<% table.column :longitude do |region| %>
<%= "#{region.longitude.abs.round(4)}° #{region.longitude >= 0 ? 'E' : 'W'}" %>
<% end %>
<% table.column :actions, header: t("shared.index_table_component.actions.label"), col_size: "65px" do |region| %>
<div class="flex flex-row gap-2 items-center justify-center">
<%= render ActionButtonComponent.new(to: edit_region_path(region), icon: "edit", turbo_stream: true, colour: :primary) %>
<%= render ActionButtonComponent.new(to: region_path(region), method: :delete, icon: "delete", colour: :error, confirm: t("regions.destroy.confirm")) %>
</div>
<% end %>
<% end %>
<% end %>
</div>
2 changes: 1 addition & 1 deletion app/views/reports/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
</button>
</div>

<%= render Shared::IndexTableComponent.new(records: @available_journals, per_page: 6) do |table| %>
<%= render Shared::IndexTableComponent.new(records: @available_journals, per_page: 6, clickable_rows: false) do |table| %>
<% table.column :title %>
<% table.column :author, header: t(".author") do |journal| %>
<%= journal.user.username %>
Expand Down
27 changes: 16 additions & 11 deletions app/views/subprojects/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<%= render ActionButtonComponent.new(to: project_subproject_path(@project, @subproject), method: :delete, icon: "delete", colour: :error, size: :large, confirm: t("subprojects.destroy.confirm")) do %>
<%= t(".actions.delete") %>
<% end %>
<%= render ActionButtonComponent.new(to: edit_project_subproject_path(@project, @subproject), icon: "edit", colour: :info, turbo_stream: true, size: :large) do %>
<%= render ActionButtonComponent.new(to: edit_project_subproject_path(@project, @subproject), icon: "edit", colour: :secondary, turbo_stream: true, size: :large) do %>
<%= t(".actions.edit") %>
<% end %>
<%= render ActionButtonComponent.new(to: new_project_subproject_log_entry_path(@project, @subproject), icon: "add", turbo_stream: true, size: :large, colour: :primary) do %>
Expand All @@ -22,7 +22,11 @@
</div>
<%= render ContentCardComponent.new do %>
<h1 class="text-2xl font-bold"><%= t(".log_entries") %></h1>
<%= render Shared::IndexTableComponent.new(records: @subproject.log_entries) do |table| %>
<%= render Shared::IndexTableComponent.new(
records: @subproject.log_entries,
turbo_stream_rows: true,
record_path: ->(log_entry) { project_subproject_log_entry_path(@project, @subproject, log_entry) }
) do |table| %>
<% table.column :created_at do |log_entry| %>
<%= l(log_entry.created_at) %>
<% end %>
Expand All @@ -32,18 +36,20 @@
<% table.column :user do |log_entry| %>
<%= log_entry.user.username %>
<% end %>
<% table.column :actions, header: t("shared.index_table_component.actions"), col_size: "90px" do |log_entry| %>
<div class="flex flex-row gap-2 items-center">
<%= render ActionButtonComponent.new(to: project_subproject_log_entry_path(@project, @subproject, log_entry), icon: "view", turbo_stream: true) %>
<%= render ActionButtonComponent.new(to: edit_project_subproject_log_entry_path(@project, @subproject, log_entry), icon: "edit", turbo_stream: true, colour: :info) %>
<% table.column :actions, header: t("shared.index_table_component.actions.label"), col_size: "65px" do |log_entry| %>
<div class="flex flex-row gap-2 items-center justify-center">
<%= render ActionButtonComponent.new(to: edit_project_subproject_log_entry_path(@project, @subproject, log_entry), icon: "edit", turbo_stream: true, colour: :primary) %>
<%= render ActionButtonComponent.new(to: project_subproject_log_entry_path(@project, @subproject, log_entry), method: :delete, icon: "delete", colour: :error, confirm: t("projects.subprojects.log_entries.destroy.confirm")) %>
</div>
<% end %>
<% end %>
<% end %>
<%= render ContentCardComponent.new do %>
<h1 class="text-2xl font-bold"><%= t(".journal_entries") %></h1>
<%= render Shared::IndexTableComponent.new(records: @subproject.journals) do |table| %>
<%= render Shared::IndexTableComponent.new(
records: @subproject.journals,
record_path: ->(journal) { project_subproject_journal_path(@project, @subproject, journal) }
) do |table| %>
<% table.column :title do |journal| %>
<%= journal.title %>
<% end %>
Expand All @@ -56,10 +62,9 @@
<% table.column :user do |journal| %>
<%= journal.user.username %>
<% end %>
<% table.column :actions, header: t("shared.index_table_component.actions"), col_size: "90px" do |journal| %>
<div class="flex flex-row gap-2 items-center">
<%= render ActionButtonComponent.new(to: project_subproject_journal_path(@project, @subproject, journal), icon: "view", turbo_stream: true) %>
<%= render ActionButtonComponent.new(to: edit_project_subproject_journal_path(@project, @subproject, journal), icon: "edit", turbo_stream: true, colour: :info) %>
<% table.column :actions, header: t("shared.index_table_component.actions.label"), col_size: "65px" do |journal| %>
<div class="flex flex-row gap-2 items-center justify-center">
<%= render ActionButtonComponent.new(to: edit_project_subproject_journal_path(@project, @subproject, journal), icon: "edit", colour: :primary) %>
<%= render ActionButtonComponent.new(to: project_subproject_journal_path(@project, @subproject, journal), method: :delete, icon: "delete", colour: :error, confirm: t("projects.subprojects.journals.destroy.confirm")) %>
</div>
<% end %>
Expand Down
12 changes: 7 additions & 5 deletions app/views/users/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
<% end %>
</div>
<%= render ContentCardComponent.new do %>
<%= render Shared::IndexTableComponent.new(records: @users) do |table| %>
<%= render Shared::IndexTableComponent.new(
records: @users,
record_path: ->(user) { user_path(user) }
) do |table| %>
<% table.column :username %>
<% table.column :roles do |user| %>
<% role_colour_map = { "admin" => :primary, "reporter" => :info, "analyst" => :warning } %>
Expand All @@ -15,10 +18,9 @@
<%= render BadgeComponent.new(text: role.role, colour: col) %>
<% end %>
<% end %>
<% table.column :actions, header: t("shared.index_table_component.actions"), col_size: "90px" do |user| %>
<div class="flex flex-row gap-2 items-center">
<%= render ActionButtonComponent.new(to: user_path(user), icon: "view") %>
<%= render ActionButtonComponent.new(to: edit_user_path(user), icon: "edit", turbo_stream: true, colour: :info) %>
<% table.column :actions, header: t("shared.index_table_component.actions.label"), col_size: "65px" do |user| %>
<div class="flex flex-row gap-2 items-center justify-center">
<%= render ActionButtonComponent.new(to: edit_user_path(user), icon: "edit", turbo_stream: true, colour: :primary) %>
<%= render ActionButtonComponent.new(to: user_path(user), method: :delete, icon: "delete", colour: :error, confirm: t("users.destroy.confirm")) %>
</div>
<% end %>
Expand Down
Loading
Loading