dbviewer 0.3.15 → 0.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65e50cbafa3a2fc1372ce4150c0de4d54001345af9e7d487b048ba3d59929fd2
4
- data.tar.gz: 3e84c14c32a912f5c447dc2d4525231f83cd2bd272f99a3b4e19261b692ee2c3
3
+ metadata.gz: 72e7257a74294ca4a6ad8b53fc497ca9eb0727772e798b2c4d7fa2c164b35531
4
+ data.tar.gz: d01a7c934e3e859f06f94a746671974dd93813e832c6f5f54df49b3071e95178
5
5
  SHA512:
6
- metadata.gz: 5d12f7e3bc158c902256ce416e7770d95436deefcb12f7a54673200d06706d57e5368a8ddd2d6ed374c5c64ebe753c82619fa505864b5f002cf805e6d3d11146
7
- data.tar.gz: 645d438dd54e867e6f58c2d0e2f1cdc09c49652b97cfd93ce39758440dea8f58e14910a5718d677513762195a5346a225c11fa289597ef6a7b28e4cf75541e2a
6
+ metadata.gz: 79511e9ac29ca71e4834b958c54b36ee05f83e831f1426745282139ea6b46d5e0e1bcd2a2b31df956987218bfc44239fa624dba86f74ec4858cc35aea4af8536
7
+ data.tar.gz: e63f51557b5f2431f63c920989a1b13f7ed5c1cbe5cd708091fc8f728e649caae11a6c09a754d6522b2dbbb3408cb380a2ded2f438770e8b9c2d49b782bdcd7f
data/README.md CHANGED
@@ -44,7 +44,8 @@ It's designed for development, debugging, and database analysis, offering a clea
44
44
  <details>
45
45
  <summary>Click to see more screenshots</summary>
46
46
 
47
- #### Dashboard Overview
47
+ #### Dashboard Overview
48
+
48
49
  <img width="1470" alt="image" src="https://github.com/user-attachments/assets/4e803d51-9a5b-4c80-bb4c-a761dba15a40" />
49
50
 
50
51
  #### Table Details
@@ -76,7 +77,7 @@ gem "dbviewer", group: :development
76
77
  And then execute:
77
78
 
78
79
  ```bash
79
- $ bundle
80
+ bundle
80
81
  ```
81
82
 
82
83
  ## 🔧 Usage
@@ -274,6 +275,7 @@ With the addition of Basic Authentication, DBViewer can now be used in any envir
274
275
  ```
275
276
 
276
277
  3. Access the tool through your regular application URL:
278
+
277
279
  ```
278
280
  https://yourdomain.com/dbviewer?override_env_check=your_secure_random_key
279
281
  ```
@@ -288,6 +290,36 @@ When used in production, ensure:
288
290
  - You access DBViewer over HTTPS connections only
289
291
  - Access is limited to trusted administrators only
290
292
 
293
+ ## 🔄 Updating DBViewer
294
+
295
+ To keep DBViewer up to date with the latest features, security patches, and bug fixes, follow these steps:
296
+
297
+ ### Using Bundler
298
+
299
+ The simplest way to update is using Bundler:
300
+
301
+ - Update your Gemfile with the desired version:
302
+
303
+ ```ruby
304
+ # For the latest version
305
+ gem "dbviewer", group: :development
306
+
307
+ # Or specify a version
308
+ gem "dbviewer", "~> 0.3.2", group: :development
309
+ ```
310
+
311
+ - Run bundle update:
312
+
313
+ ```ruby
314
+ bundle update dbviewer
315
+ ```
316
+
317
+ - Restart your Rails server to apply the changes:
318
+
319
+ ```ruby
320
+ rails server
321
+ ```
322
+
291
323
  ## 🤌🏻 Contributing
292
324
 
293
325
  Bug reports and pull requests are welcome.
@@ -11,6 +11,12 @@ module Dbviewer
11
11
  @database_manager ||= ::Dbviewer::DatabaseManager.new
12
12
  end
13
13
 
14
+ # Initialize the table query operations manager
15
+ # This gives direct access to table query operations when needed
16
+ def table_query_operations
17
+ @table_query_operations ||= database_manager.table_query_operations
18
+ end
19
+
14
20
  # Get the name of the current database
15
21
  def get_database_name
16
22
  adapter = database_manager.connection.adapter_name.downcase
@@ -151,19 +157,8 @@ module Dbviewer
151
157
  end
152
158
 
153
159
  # Fetch records for a table with pagination and sorting
154
- def fetch_table_records(table_name)
155
- column_filters = params[:column_filters] || {}
156
- # Clean up blank filters
157
- column_filters.reject! { |_, v| v.blank? }
158
-
159
- database_manager.table_records(
160
- table_name,
161
- @current_page,
162
- @order_by,
163
- @order_direction,
164
- @per_page,
165
- column_filters || {}
166
- )
160
+ def fetch_table_records(table_name, query_params)
161
+ database_manager.table_records(table_name, query_params)
167
162
  end
168
163
 
169
164
  # Get filtered record count for a table
@@ -377,17 +372,24 @@ module Dbviewer
377
372
  end
378
373
 
379
374
  # Export table data to CSV
380
- def export_table_to_csv(table_name, limit = 10000, include_headers = true)
375
+ def export_table_to_csv(table_name, query_params = nil, include_headers = true)
381
376
  require "csv"
382
377
 
383
378
  begin
384
- records = database_manager.table_records(
385
- table_name,
386
- 1, # First page
387
- nil, # Default sorting
388
- "asc",
389
- limit # Limit number of records
390
- )
379
+ if query_params.is_a?(Dbviewer::TableQueryParams)
380
+ # Use the query params object directly
381
+ records = database_manager.query_operations.table_records(table_name, query_params)
382
+ else
383
+ # Legacy support for the old method signature
384
+ limit = query_params.is_a?(Numeric) ? query_params : 10000
385
+ records = database_manager.table_records(
386
+ table_name,
387
+ 1, # First page
388
+ nil, # Default sorting
389
+ "asc",
390
+ limit # Limit number of records
391
+ )
392
+ end
391
393
 
392
394
  csv_data = CSV.generate do |csv|
393
395
  # Add headers if requested
@@ -35,6 +35,14 @@ module Dbviewer
35
35
  @order_direction = "ASC" unless self.class::VALID_SORT_DIRECTIONS.include?(@order_direction)
36
36
  end
37
37
 
38
+ def fetch_total_count(table_name, query_params)
39
+ if query_params.column_filters.present? && query_params.column_filters.values.any?(&:present?)
40
+ fetch_filtered_record_count(table_name, query_params.column_filters)
41
+ else
42
+ fetch_table_record_count(table_name)
43
+ end
44
+ end
45
+
38
46
  # Calculate the total number of pages
39
47
  def calculate_total_pages(total_count, per_page)
40
48
  (total_count.to_f / per_page).ceil
@@ -2,34 +2,38 @@ module Dbviewer
2
2
  class TablesController < ApplicationController
3
3
  include Dbviewer::PaginationConcern
4
4
 
5
+ before_action :set_table_name, except: [ :index ]
6
+
5
7
  def index
6
8
  @tables = fetch_tables_with_stats(include_record_counts: true)
7
9
  end
8
10
 
9
11
  def show
10
- @table_name = params[:id]
11
- @columns = fetch_table_columns(@table_name)
12
- @metadata = fetch_table_metadata(@table_name)
13
- @tables = fetch_tables_with_stats # Fetch tables for sidebar
14
-
15
12
  set_pagination_params
16
13
  set_sorting_params
17
14
 
18
- # Extract column filters from params
15
+ # Get column filters from params first
19
16
  @column_filters = params[:column_filters].presence ? params[:column_filters].to_enum.to_h : {}
20
17
 
21
- if @column_filters.present? && @column_filters.values.any?(&:present?)
22
- @total_count = fetch_filtered_record_count(@table_name, @column_filters)
23
- else
24
- @total_count = fetch_table_record_count(@table_name)
25
- end
26
-
18
+ # Then apply global creation filters (this will modify @column_filters)
19
+ set_global_filters
20
+
21
+ # Now create the query params with the combined filters
22
+ query_params = Dbviewer::TableQueryParams.new(
23
+ page: @current_page,
24
+ per_page: @per_page,
25
+ order_by: @order_by,
26
+ direction: @order_direction,
27
+ column_filters: @column_filters.reject { |_, v| v.blank? }
28
+ )
29
+ @total_count = fetch_total_count(@table_name, query_params)
30
+ @records = fetch_table_records(@table_name, query_params)
27
31
  @total_pages = calculate_total_pages(@total_count, @per_page)
28
- @records = fetch_table_records(@table_name)
32
+ @columns = fetch_table_columns(@table_name)
33
+ @metadata = fetch_table_metadata(@table_name)
29
34
 
30
- # Ensure @records is never nil to prevent template errors
31
35
  if @records.nil?
32
- column_names = fetch_table_columns(@table_name).map { |c| c[:name] }
36
+ column_names = @columns.map { |c| c[:name] }
33
37
  @records = ActiveRecord::Result.new(column_names, [])
34
38
  end
35
39
 
@@ -53,8 +57,6 @@ module Dbviewer
53
57
  end
54
58
 
55
59
  def mini_erd
56
- @table_name = params[:id]
57
-
58
60
  begin
59
61
  @erd_data = fetch_mini_erd_for_table(@table_name)
60
62
 
@@ -81,7 +83,6 @@ module Dbviewer
81
83
  end
82
84
 
83
85
  def query
84
- @table_name = params[:id]
85
86
  @read_only_mode = true # Flag to indicate we're in read-only mode
86
87
  @columns = fetch_table_columns(@table_name)
87
88
  @tables = fetch_tables_with_stats # Fetch tables for sidebar
@@ -102,20 +103,98 @@ module Dbviewer
102
103
  return
103
104
  end
104
105
 
105
- table_name = params[:id]
106
106
  limit = (params[:limit] || 10000).to_i
107
107
  include_headers = params[:include_headers] != "0"
108
108
 
109
- csv_data = export_table_to_csv(table_name, limit, include_headers)
109
+ # Apply global creation filters
110
+ set_global_filters
111
+
112
+ # Create query parameters for export
113
+ query_params = Dbviewer::TableQueryParams.new(
114
+ page: 1,
115
+ per_page: limit,
116
+ order_by: nil,
117
+ direction: "asc",
118
+ column_filters: @column_filters.reject { |_, v| v.blank? }
119
+ )
120
+
121
+ # Get filtered data for export
122
+ csv_data = export_table_to_csv(@table_name, query_params, include_headers)
110
123
 
111
124
  # Set filename with timestamp for uniqueness
112
125
  timestamp = Time.now.strftime("%Y%m%d%H%M%S")
113
- filename = "#{table_name}_export_#{timestamp}.csv"
126
+
127
+ # Add filter info to filename if filters are applied
128
+ filename_suffix = ""
129
+ if @creation_filter_start.present? || @creation_filter_end.present?
130
+ filename_suffix = "_filtered"
131
+ end
132
+
133
+ filename = "#{@table_name}#{filename_suffix}_#{timestamp}.csv"
114
134
 
115
135
  # Send data as file
116
136
  send_data csv_data,
117
137
  type: "text/csv; charset=utf-8; header=present",
118
138
  disposition: "attachment; filename=#{filename}"
119
139
  end
140
+
141
+ private
142
+
143
+ def set_table_name
144
+ @table_name = params[:id]
145
+ end # Handle global creation datetime filters across tables
146
+ def set_global_filters
147
+ # Store creation filter datetimes in session to persist between table navigation
148
+ if params[:creation_filter_start].present?
149
+ session[:creation_filter_start] = params[:creation_filter_start]
150
+ end
151
+
152
+ if params[:creation_filter_end].present?
153
+ session[:creation_filter_end] = params[:creation_filter_end]
154
+ end
155
+
156
+ # Clear filters if explicitly requested
157
+ if params[:clear_creation_filter] == "true"
158
+ session.delete(:creation_filter_start)
159
+ session.delete(:creation_filter_end)
160
+ end
161
+
162
+ # Set instance variables for view access
163
+ @creation_filter_start = session[:creation_filter_start]
164
+ @creation_filter_end = session[:creation_filter_end]
165
+
166
+ # Initialize column_filters if not present
167
+ @column_filters ||= {}
168
+
169
+ # Apply creation filters to column_filters if the table has a created_at column
170
+ if has_timestamp_column?(@table_name) && (@creation_filter_start.present? || @creation_filter_end.present?)
171
+ # Clear any existing created_at filters to avoid conflicts
172
+ @column_filters.delete("created_at")
173
+ @column_filters.delete("created_at_operator")
174
+ @column_filters.delete("created_at_end")
175
+
176
+ if @creation_filter_start.present? && @creation_filter_end.present?
177
+ # If both start and end are present, set up a range filter
178
+ @column_filters["created_at"] = @creation_filter_start
179
+ @column_filters["created_at_end"] = @creation_filter_end
180
+ # No need for operator when using start and end together
181
+ Rails.logger.info("[DBViewer] Setting creation filter range: #{@creation_filter_start} to #{@creation_filter_end}")
182
+ elsif @creation_filter_start.present?
183
+ # Only start date is present
184
+ @column_filters["created_at"] = @creation_filter_start
185
+ @column_filters["created_at_operator"] = "gte" # Greater than or equal
186
+ Rails.logger.info("[DBViewer] Setting creation filter start: #{@creation_filter_start}")
187
+ elsif @creation_filter_end.present?
188
+ # Only end date is present
189
+ @column_filters["created_at"] = @creation_filter_end
190
+ @column_filters["created_at_operator"] = "lte" # Less than or equal
191
+ Rails.logger.info("[DBViewer] Setting creation filter end: #{@creation_filter_end}")
192
+ end
193
+ else
194
+ if @creation_filter_start.present? || @creation_filter_end.present?
195
+ Rails.logger.info("[DBViewer] Creation filter present but table #{@table_name} has no created_at column")
196
+ end
197
+ end
198
+ end
120
199
  end
121
200
  end
@@ -1,5 +1,19 @@
1
1
  module Dbviewer
2
2
  module ApplicationHelper
3
+ # Check if a table has a created_at column
4
+ def has_timestamp_column?(table_name)
5
+ return false unless table_name.present?
6
+
7
+ # Get the columns for the table directly using DatabaseManager
8
+ columns = get_database_manager.table_columns(table_name)
9
+ columns.any? { |col| col[:name] == "created_at" && [ :datetime, :timestamp ].include?(col[:type]) }
10
+ end
11
+
12
+ # Helper to access the database manager
13
+ def get_database_manager
14
+ @database_manager ||= ::Dbviewer::DatabaseManager.new
15
+ end
16
+
3
17
  def format_cell_value(value)
4
18
  return "NULL" if value.nil?
5
19
  return value.to_s.truncate(100) unless value.is_a?(String)
@@ -114,5 +128,64 @@ module Dbviewer
114
128
  def logs_nav_class
115
129
  active_nav_class("logs")
116
130
  end
131
+
132
+ # Returns a sort icon based on the current sort direction
133
+ def sort_icon(column_name, current_order_by, current_direction)
134
+ if column_name == current_order_by
135
+ direction = current_direction == "ASC" ? "up" : "down"
136
+ "<i class='bi bi-sort-#{direction}'></i>".html_safe
137
+ else
138
+ "<i class='bi bi-filter invisible sort-icon'></i>".html_safe
139
+ end
140
+ end
141
+
142
+ # Determine the next sort direction based on the current one
143
+ def next_sort_direction(column_name, current_order_by, current_direction)
144
+ if column_name == current_order_by && current_direction == "ASC"
145
+ "DESC"
146
+ else
147
+ "ASC"
148
+ end
149
+ end
150
+
151
+ # Generate a sortable column header link
152
+ def sortable_column_header(column_name, current_order_by, current_direction, table_name, current_page, per_page, column_filters)
153
+ is_sorted = column_name == current_order_by
154
+ sort_direction = next_sort_direction(column_name, current_order_by, current_direction)
155
+
156
+ aria_sort = if is_sorted
157
+ current_direction.downcase == "asc" ? "ascending" : "descending"
158
+ else
159
+ "none"
160
+ end
161
+
162
+ # Build parameters for the sort link
163
+ sort_params = {
164
+ order_by: column_name,
165
+ order_direction: sort_direction,
166
+ page: current_page,
167
+ per_page: per_page,
168
+ column_filters: column_filters
169
+ }
170
+
171
+ # Add creation filter parameters if they're in the controller
172
+ if defined?(@creation_filter_start) && @creation_filter_start.present?
173
+ sort_params[:creation_filter_start] = @creation_filter_start
174
+ end
175
+
176
+ if defined?(@creation_filter_end) && @creation_filter_end.present?
177
+ sort_params[:creation_filter_end] = @creation_filter_end
178
+ end
179
+
180
+ link_to table_path(table_name, sort_params),
181
+ class: "d-flex align-items-center text-decoration-none text-reset column-sort-link",
182
+ title: "Sort by #{column_name} (#{sort_direction.downcase})",
183
+ "aria-sort": aria_sort,
184
+ role: "button",
185
+ tabindex: "0" do
186
+ content_tag(:span, column_name, class: "column-name") +
187
+ content_tag(:span, sort_icon(column_name, current_order_by, current_direction), class: "sort-icon-container")
188
+ end
189
+ end
117
190
  end
118
191
  end
@@ -5,13 +5,158 @@
5
5
  id="tableSearch" placeholder="Filter tables..." aria-label="Filter tables">
6
6
  </div>
7
7
 
8
+ <div class="p-2">
9
+ <div class="accordion accordion-flush" id="creationFilterAccordion">
10
+ <div class="accordion-item border-0">
11
+ <h2 class="accordion-header" id="creationFilterHeading">
12
+ <button class="accordion-button p-2 collapsed" type="button" data-bs-toggle="collapse"
13
+ data-bs-target="#creationFilterCollapse" aria-expanded="false"
14
+ aria-controls="creationFilterCollapse">
15
+ <i class="bi bi-calendar-range me-2"></i> Creation Filter
16
+ <% if @creation_filter_start.present? || @creation_filter_end.present? %>
17
+ <% if @table_name.present? && has_timestamp_column?(@table_name) %>
18
+ <span class="badge bg-success ms-2">Active</span>
19
+ <% else %>
20
+ <span class="badge bg-secondary ms-2">Set</span>
21
+ <% end %>
22
+ <% end %>
23
+ </button>
24
+ </h2>
25
+ <div id="creationFilterCollapse" class="accordion-collapse collapse" aria-labelledby="creationFilterHeading">
26
+ <div class="accordion-body p-2">
27
+ <form id="creationFilterForm" action="<%= request.path %>" method="get" class="mb-0">
28
+ <!-- Preserve existing query parameters -->
29
+ <input type="hidden" name="page" value="<%= @current_page %>">
30
+ <input type="hidden" name="per_page" value="<%= @per_page %>">
31
+ <input type="hidden" name="order_by" value="<%= @order_by %>">
32
+ <input type="hidden" name="order_direction" value="<%= @order_direction %>">
33
+
34
+ <!-- Datetime range fields -->
35
+ <div class="mb-2">
36
+ <label for="creationFilterStart" class="form-label mb-1 small">Start Date/Time</label>
37
+ <input type="datetime-local" id="creationFilterStart" name="creation_filter_start"
38
+ class="form-control form-control-sm" value="<%= @creation_filter_start %>">
39
+ </div>
40
+ <div class="mb-2">
41
+ <label for="creationFilterEnd" class="form-label mb-1 small">End Date/Time</label>
42
+ <input type="datetime-local" id="creationFilterEnd" name="creation_filter_end"
43
+ class="form-control form-control-sm" value="<%= @creation_filter_end %>">
44
+ </div>
45
+
46
+ <div class="d-flex justify-content-between">
47
+ <button type="submit" class="btn btn-primary btn-sm">Apply</button>
48
+ <% if @creation_filter_start.present? || @creation_filter_end.present? %>
49
+ <%
50
+ # Preserve other query params when clearing creation filter
51
+ clear_params = {
52
+ clear_creation_filter: true,
53
+ page: @current_page,
54
+ per_page: @per_page,
55
+ order_by: @order_by,
56
+ order_direction: @order_direction
57
+ }
58
+ %>
59
+ <a href="<%= request.path %>?<%= clear_params.to_query %>" class="btn btn-outline-secondary btn-sm">Clear</a>
60
+ <% end %>
61
+ </div>
62
+ <div class="mt-2 small">
63
+ <% if @table_name.present? && has_timestamp_column?(@table_name) && (@creation_filter_start.present? || @creation_filter_end.present?) %>
64
+ <div class="text-success">
65
+ <i class="bi bi-check-circle-fill"></i>
66
+ Filter active on this table.
67
+ <% if @current_page == 1 && @records && @records.rows && @records.rows.empty? %>
68
+ <div class="alert alert-warning p-1 mt-2 small">
69
+ <i class="bi bi-exclamation-triangle-fill"></i>
70
+ No records match the filter criteria.
71
+ </div>
72
+ <% end %>
73
+ </div>
74
+ <% elsif @table_name.present? && (@creation_filter_start.present? || @creation_filter_end.present?) %>
75
+ <div class="text-warning">
76
+ <i class="bi bi-exclamation-circle-fill"></i>
77
+ This table has no <code>created_at</code> column.
78
+ </div>
79
+ <% elsif !@table_name.present? && (@creation_filter_start.present? || @creation_filter_end.present?) %>
80
+ <div class="text-info">
81
+ <i class="bi bi-info-circle-fill"></i>
82
+ Filter will be applied on tables with <code>created_at</code> column.
83
+ </div>
84
+ <% else %>
85
+ <div class="text-muted">
86
+ <i class="bi bi-info-circle-fill"></i>
87
+ Filters apply to tables with a <code>created_at</code> column.
88
+ </div>
89
+ <% end %>
90
+ </div>
91
+ </form>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+
98
+ <!-- Add custom styling for datetime inputs -->
99
+ <style>
100
+ /* Better datetime input styling */
101
+ input[type="datetime-local"] {
102
+ padding-right: 0.5rem;
103
+ }
104
+
105
+ /* Dark mode support for datetime inputs */
106
+ [data-bs-theme="dark"] input[type="datetime-local"] {
107
+ background-color: rgba(255,255,255,0.1);
108
+ color: #fff;
109
+ border-color: rgba(255,255,255,0.15);
110
+ }
111
+
112
+ [data-bs-theme="dark"] input[type="datetime-local"]::-webkit-calendar-picker-indicator {
113
+ filter: invert(1);
114
+ }
115
+ </style>
116
+
117
+ <script>
118
+ // Set default values for datetime inputs when empty
119
+ document.addEventListener('DOMContentLoaded', function() {
120
+ const startInput = document.getElementById('creationFilterStart');
121
+ const endInput = document.getElementById('creationFilterEnd');
122
+
123
+ // When applying filter with empty start date, default to beginning of current month
124
+ if (startInput) {
125
+ startInput.addEventListener('click', function() {
126
+ if (!this.value) {
127
+ const now = new Date();
128
+ const firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
129
+ const formattedDate = firstDay.toISOString().slice(0, 16); // Format: YYYY-MM-DDTHH:MM
130
+ this.value = formattedDate;
131
+ }
132
+ });
133
+ }
134
+
135
+ // When applying filter with empty end date, default to current datetime
136
+ if (endInput) {
137
+ endInput.addEventListener('click', function() {
138
+ if (!this.value) {
139
+ const now = new Date();
140
+ const formattedDate = now.toISOString().slice(0, 16); // Format: YYYY-MM-DDTHH:MM
141
+ this.value = formattedDate;
142
+ }
143
+ });
144
+ }
145
+ });
146
+ </script>
8
147
  </div>
9
148
 
10
149
  <div class="dbviewer-sidebar-content">
11
150
  <% if @tables.any? %>
12
151
  <div class="list-group list-group-flush" id="tablesList">
13
152
  <% @tables.each do |table| %>
14
- <%= link_to table_path(table[:name]), title: table[:name],
153
+ <%
154
+ # Build table URL with creation filter params if they exist
155
+ table_url_params = {}
156
+ table_url_params[:creation_filter_start] = @creation_filter_start if @creation_filter_start.present?
157
+ table_url_params[:creation_filter_end] = @creation_filter_end if @creation_filter_end.present?
158
+ %>
159
+ <%= link_to table_path(table[:name], table_url_params), title: table[:name],
15
160
  class: "list-group-item list-group-item-action d-flex align-items-center #{'active' if current_table?(table[:name])}",
16
161
  tabindex: "0",
17
162
  data: { table_name: table[:name] },
@@ -44,7 +44,13 @@
44
44
  <% @tables.each do |table| %>
45
45
  <tr>
46
46
  <td class="fw-medium">
47
- <%= link_to table[:name], table_path(table[:name]), class: "text-decoration-none" %>
47
+ <%
48
+ # Include creation filter params in table links
49
+ filter_params = {}
50
+ filter_params[:creation_filter_start] = session[:creation_filter_start] if session[:creation_filter_start].present?
51
+ filter_params[:creation_filter_end] = session[:creation_filter_end] if session[:creation_filter_end].present?
52
+ %>
53
+ <%= link_to table[:name], table_path(table[:name], filter_params), class: "text-decoration-none" %>
48
54
  </td>
49
55
  <td class="text-end">
50
56
  <span class="badge bg-secondary-subtle"><%= table[:record_count] %></span>