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
15 changes: 15 additions & 0 deletions app/controllers/paper_trail_manager/changes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ def index
@versions = @versions.where(item_type: params[:type]) if params[:type]
@versions = @versions.where(item_id: params[:id]) if params[:id]

# Date range filtering
@from = parse_date(params[:from])
@to = parse_date(params[:to])
@versions = @versions.where('created_at >= ?', @from.beginning_of_day) if @from
@versions = @versions.where('created_at <= ?', @to.end_of_day) if @to

# Ensure pagination parameters have sensible values
@page = params[:page].to_i
@page = nil if @page.zero?
Expand Down Expand Up @@ -127,5 +133,14 @@ def change_revert_allowed?(version)
PaperTrailManager.allow_revert?(self, version)
end
helper_method :change_revert_allowed?

# Parse a date string, returning nil for invalid/missing values
def parse_date(value)
return nil if value.blank?

Date.parse(value)
rescue Date::Error, ArgumentError
nil
end
end
end
13 changes: 13 additions & 0 deletions app/views/paper_trail_manager/changes/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
Show:
<%= ([link_to('All', changes_path)] + change_item_types.map { |type| link_to(type.pluralize, changes_path(type: type)) }).join(' | ').html_safe %>
</p>

<%= form_tag changes_path, method: :get, class: 'changes_date_filter' do %>
<%= hidden_field_tag :type, params[:type] if params[:type] %>
<%= hidden_field_tag :id, params[:id] if params[:id] %>
<label for="from">From:</label>
<%= date_field_tag :from, params[:from], id: 'from' %>
<label for="to">To:</label>
<%= date_field_tag :to, params[:to], id: 'to' %>
<%= submit_tag 'Filter', name: nil %>
<% if params[:from].present? || params[:to].present? %>
<%= link_to 'Clear', changes_path(type: params[:type], id: params[:id]), class: 'clear_filter' %>
<% end %>
<% end %>
<div class="table-responsive">
<table class='changes_table table table-bordered'>
<tfoot>
Expand Down
84 changes: 84 additions & 0 deletions spec/integration/date_filter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# frozen_string_literal: true

require 'spec_helper'

describe PaperTrailManager, 'date range filtering', versioning: true do
let(:entity) { FactoryBot.create(:entity, name: 'Test Entity', status: 'Active') }

before do
entity
entity.update(status: 'Updated')
end

describe 'index with date filters' do
context 'with from parameter' do
it 'shows changes on or after the date' do
get changes_path(from: Date.today.to_s)
expect(response).to have_http_status(:ok)
expect(response.body).to have_tag('.change_row')
end

it 'returns no changes for future date' do
get changes_path(from: (Date.today + 1).to_s)
expect(response.body).to include('No changes found')
end
end

context 'with to parameter' do
it 'shows changes on or before the date' do
get changes_path(to: Date.today.to_s)
expect(response).to have_http_status(:ok)
expect(response.body).to have_tag('.change_row')
end

it 'returns no changes for past date' do
get changes_path(to: (Date.today - 1).to_s)
expect(response.body).to include('No changes found')
end
end

context 'with from and to combined' do
it 'shows changes within the range' do
get changes_path(from: Date.today.to_s, to: Date.today.to_s)
expect(response).to have_http_status(:ok)
expect(response.body).to have_tag('.change_row')
end
end

context 'combined with type filter' do
it 'respects both date and type filters' do
get changes_path(from: Date.today.to_s, type: 'Entity')
expect(response).to have_http_status(:ok)
expect(response.body).to have_tag('.change_item', text: /Entity/)
end
end

context 'with invalid date' do
it 'ignores invalid from date gracefully' do
get changes_path(from: 'not-a-date')
expect(response).to have_http_status(:ok)
expect(response.body).to have_tag('.change_row')
end

it 'ignores invalid to date gracefully' do
get changes_path(to: 'garbage')
expect(response).to have_http_status(:ok)
expect(response.body).to have_tag('.change_row')
end
end

context 'date filter form' do
it 'renders date inputs' do
get changes_path
expect(response.body).to have_tag('input[type="date"][name="from"]')
expect(response.body).to have_tag('input[type="date"][name="to"]')
end

it 'preserves date values in form' do
get changes_path(from: '2026-01-01', to: '2026-12-31')
expect(response.body).to have_tag('input[name="from"][value="2026-01-01"]')
expect(response.body).to have_tag('input[name="to"][value="2026-12-31"]')
end
end
end
end
Loading