recycle_bin 1.1.0 → 1.2.0
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 +4 -4
- data/CHANGELOG.md +57 -0
- data/README.md +228 -13
- data/app/controllers/recycle_bin/trash_controller.rb +424 -21
- data/app/helpers/recycle_bin/application_helper.rb +175 -0
- data/app/views/{layouts/recycle_bin/application.html.erb → recycle_bin/layouts/recycle_bin.html.erb} +850 -609
- data/app/views/recycle_bin/shared/_error_messages.html.erb +10 -0
- data/app/views/recycle_bin/shared/_flash_messages.html.erb +6 -0
- data/app/views/recycle_bin/trash/_action_history.html.erb +75 -0
- data/app/views/recycle_bin/trash/_associations.html.erb +50 -0
- data/app/views/recycle_bin/trash/_filters.html.erb +561 -0
- data/app/views/recycle_bin/trash/_item.html.erb +267 -0
- data/app/views/recycle_bin/trash/_pagination.html.erb +75 -0
- data/app/views/recycle_bin/trash/_stats.html.erb +50 -0
- data/app/views/recycle_bin/trash/dashboard.html.erb +618 -0
- data/app/views/recycle_bin/trash/index.html.erb +247 -278
- data/app/views/recycle_bin/trash/show.html.erb +60 -215
- data/config/routes.rb +9 -2
- data/docs/index.html +928 -0
- data/docs/logo.svg +71 -0
- data/lib/recycle_bin/version.rb +1 -1
- data/lib/recycle_bin.rb +111 -1
- metadata +18 -8
- data/app/views/layouts/recycle_bin/recycle_bin/application.html.erb +0 -266
- data/app/views/layouts/recycle_bin/recycle_bin/trash/index.html.erb +0 -133
- data/app/views/layouts/recycle_bin/recycle_bin/trash/show.html.erb +0 -175
- data/recycle_bin.gemspec +0 -47
|
@@ -1,320 +1,289 @@
|
|
|
1
|
-
|
|
1
|
+
<% content_for :title, "Deleted Items" %>
|
|
2
2
|
|
|
3
|
-
<!--
|
|
4
|
-
<div
|
|
5
|
-
<div
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
<div style="font-size: 0.8rem; margin-top: 8px; color: #28a745;">
|
|
9
|
-
<%= @filtered_items.items.select { |item| item.deleted_at > 1.day.ago }.count %> deleted today
|
|
10
|
-
</div>
|
|
11
|
-
</div>
|
|
12
|
-
|
|
13
|
-
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); border-left: 4px solid #667eea;">
|
|
14
|
-
<div style="font-size: 2rem; font-weight: bold; color: #667eea; margin-bottom: 8px;"><%= @model_types.count %></div>
|
|
15
|
-
<div style="color: #6c757d; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.5px;">Model Types</div>
|
|
16
|
-
<div style="font-size: 0.8rem; margin-top: 8px;">
|
|
17
|
-
<% if @model_types.any? %>
|
|
18
|
-
<%= @model_types.join(', ') %>
|
|
19
|
-
<% else %>
|
|
20
|
-
No types
|
|
21
|
-
<% end %>
|
|
22
|
-
</div>
|
|
3
|
+
<!-- Page Header -->
|
|
4
|
+
<div class="page-header">
|
|
5
|
+
<div class="page-title-section">
|
|
6
|
+
<h1 class="page-title">🗂️ All Items</h1>
|
|
7
|
+
<p class="page-subtitle">Browse and manage all deleted items with advanced filtering and search</p>
|
|
23
8
|
</div>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<div style="font-size: 2rem; font-weight: bold; color: #667eea; margin-bottom: 8px;">
|
|
27
|
-
<%= @filtered_items.items.select { |item| item.deleted_at > 7.days.ago }.count %>
|
|
28
|
-
</div>
|
|
29
|
-
<div style="color: #6c757d; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.5px;">This Week</div>
|
|
30
|
-
<div style="font-size: 0.8rem; margin-top: 8px;">
|
|
31
|
-
Recent activity
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
<div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); border-left: 4px solid #667eea;">
|
|
36
|
-
<div style="font-size: 2rem; font-weight: bold; color: #667eea; margin-bottom: 8px;"><%= @current_page %> / <%= @total_pages %></div>
|
|
37
|
-
<div style="color: #6c757d; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.5px;">Current Page</div>
|
|
38
|
-
<div style="font-size: 0.8rem; margin-top: 8px;">
|
|
39
|
-
Showing <%= (@current_page - 1) * @per_page + 1 %>-<%= [(@current_page * @per_page), @total_count].min %> of <%= number_with_delimiter(@total_count) %>
|
|
40
|
-
</div>
|
|
9
|
+
<div class="page-actions">
|
|
10
|
+
<%= link_to "← Back to Dashboard", recycle_bin.root_path, class: "btn btn-outline" %>
|
|
41
11
|
</div>
|
|
42
12
|
</div>
|
|
43
13
|
|
|
44
|
-
<!--
|
|
45
|
-
<% if
|
|
46
|
-
<div
|
|
47
|
-
<div
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<% @model_types.each do |model_type| %>
|
|
52
|
-
<%= link_to model_type, recycle_bin.root_path(type: model_type, page: 1),
|
|
53
|
-
style: "display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border: 1px solid #dee2e6; border-radius: 6px; text-decoration: none; font-size: 14px; font-weight: 500; #{params[:type] == model_type ? 'background: #667eea; color: white;' : 'color: #495057;'}" %>
|
|
14
|
+
<!-- Search Results Summary -->
|
|
15
|
+
<% if params[:search].present? %>
|
|
16
|
+
<div class="search-summary">
|
|
17
|
+
<div class="search-summary-content">
|
|
18
|
+
<h3>🔍 <%= search_result_summary(@total_count, params[:search]) %></h3>
|
|
19
|
+
<% if @total_count > 0 %>
|
|
20
|
+
<p class="search-tip">💡 Tip: Use advanced filters to narrow down your results further</p>
|
|
54
21
|
<% end %>
|
|
55
22
|
</div>
|
|
56
|
-
|
|
57
|
-
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 15px;">
|
|
58
|
-
<span style="font-weight: 500; color: #495057;">Time:</span>
|
|
59
|
-
<%= link_to "Today", recycle_bin.root_path(time: 'today', page: 1),
|
|
60
|
-
style: "display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border: 1px solid #dee2e6; border-radius: 6px; text-decoration: none; font-size: 14px; font-weight: 500; #{params[:time] == 'today' ? 'background: #667eea; color: white;' : 'color: #495057;'}" %>
|
|
61
|
-
<%= link_to "This Week", recycle_bin.root_path(time: 'week', page: 1),
|
|
62
|
-
style: "display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border: 1px solid #dee2e6; border-radius: 6px; text-decoration: none; font-size: 14px; font-weight: 500; #{params[:time] == 'week' ? 'background: #667eea; color: white;' : 'color: #495057;'}" %>
|
|
63
|
-
<%= link_to "This Month", recycle_bin.root_path(time: 'month', page: 1),
|
|
64
|
-
style: "display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border: 1px solid #dee2e6; border-radius: 6px; text-decoration: none; font-size: 14px; font-weight: 500; #{params[:time] == 'month' ? 'background: #667eea; color: white;' : 'color: #495057;'}" %>
|
|
65
|
-
</div>
|
|
66
23
|
</div>
|
|
67
24
|
<% end %>
|
|
68
25
|
|
|
26
|
+
<!-- Filter Breadcrumb -->
|
|
27
|
+
<% if any_filters_active? %>
|
|
28
|
+
<div class="filter-breadcrumb">
|
|
29
|
+
<span class="breadcrumb-label">📋 Active Filters:</span>
|
|
30
|
+
<span class="breadcrumb-content"><%= filter_breadcrumb %></span>
|
|
31
|
+
<span class="filter-count-badge"><%= active_filter_count %></span>
|
|
32
|
+
</div>
|
|
33
|
+
<% end %>
|
|
34
|
+
|
|
35
|
+
<%= render 'recycle_bin/trash/stats',
|
|
36
|
+
total_count: @total_count,
|
|
37
|
+
model_types: @model_types,
|
|
38
|
+
filtered_items: @filtered_items,
|
|
39
|
+
current_page: @current_page,
|
|
40
|
+
per_page: @per_page,
|
|
41
|
+
total_pages: @total_pages %>
|
|
42
|
+
|
|
43
|
+
<%= render 'recycle_bin/trash/filters', model_types: @model_types %>
|
|
44
|
+
|
|
69
45
|
<% if @deleted_items.any? %>
|
|
70
|
-
<div
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
46
|
+
<div class="main-card">
|
|
47
|
+
<div class="card-header">
|
|
48
|
+
<h3 class="card-title">
|
|
49
|
+
Deleted Items
|
|
50
|
+
<% if params[:search].present? %>
|
|
51
|
+
<span class="search-highlight">for "<%= params[:search] %>"</span>
|
|
52
|
+
<% end %>
|
|
53
|
+
(<%= number_with_delimiter(@total_count) %>)
|
|
54
|
+
</h3>
|
|
55
|
+
<div class="card-actions">
|
|
56
|
+
<!-- Export buttons -->
|
|
57
|
+
<div class="export-buttons">
|
|
58
|
+
<%= link_to "📊 CSV Export", recycle_bin.trash_index_path(format: :csv, **request.query_parameters),
|
|
59
|
+
class: "btn btn-outline btn-sm",
|
|
60
|
+
title: "Export current results to CSV" %>
|
|
61
|
+
<%= link_to "📄 JSON Export", recycle_bin.trash_index_path(format: :json, **request.query_parameters),
|
|
62
|
+
class: "btn btn-outline btn-sm",
|
|
63
|
+
title: "Export current results to JSON" %>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div id="bulk-actions" class="bulk-actions">
|
|
69
|
+
<span id="bulk-count">0 items selected</span>
|
|
70
|
+
<div class="card-actions">
|
|
71
|
+
<%= form_with url: bulk_restore_trash_index_path, method: :patch, local: true do |form| %>
|
|
77
72
|
<input type="hidden" id="bulk-restore-items" name="selected_items" value="">
|
|
78
|
-
<%= form.submit "↶ Restore Selected",
|
|
79
|
-
style: "display: inline-flex; align-items: center; gap: 4px; padding: 8px 16px; background: #28a745; color: white; border: none; border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer;",
|
|
80
|
-
onclick: "return handleBulkAction('restore')" %>
|
|
73
|
+
<%= form.submit "↶ Restore Selected", class: "btn btn-success btn-sm", onclick: "return handleBulkAction('restore')" %>
|
|
81
74
|
<% end %>
|
|
82
|
-
|
|
83
|
-
<%= form_with url: recycle_bin.bulk_destroy_trash_index_path, method: :delete, local: true, style: "display: inline;" do |form| %>
|
|
75
|
+
<%= form_with url: bulk_destroy_trash_index_path, method: :delete, local: true do |form| %>
|
|
84
76
|
<input type="hidden" id="bulk-destroy-items" name="selected_items" value="">
|
|
85
|
-
<%= form.submit "🗑️ Delete Selected",
|
|
86
|
-
style: "display: inline-flex; align-items: center; gap: 4px; padding: 8px 16px; background: #dc3545; color: white; border: none; border-radius: 6px; font-size: 14px; font-weight: 500; cursor: pointer;",
|
|
87
|
-
onclick: "return handleBulkAction('destroy')" %>
|
|
77
|
+
<%= form.submit "🗑️ Delete Selected", class: "btn btn-danger btn-sm", onclick: "return handleBulkAction('destroy')" %>
|
|
88
78
|
<% end %>
|
|
89
79
|
</div>
|
|
90
80
|
</div>
|
|
91
81
|
|
|
92
|
-
<
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
<
|
|
96
|
-
<input type="checkbox" id="select-all"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
</tr>
|
|
103
|
-
</thead>
|
|
104
|
-
<tbody>
|
|
105
|
-
<% @deleted_items.each do |item| %>
|
|
106
|
-
<tr style="border-bottom: 1px solid #dee2e6;">
|
|
107
|
-
<td style="padding: 16px;">
|
|
108
|
-
<input type="checkbox" class="item-checkbox" value="<%= item.class.name %>:<%= item.id %>" style="cursor: pointer;" onchange="updateBulkActions()">
|
|
109
|
-
</td>
|
|
110
|
-
<td style="padding: 16px;">
|
|
111
|
-
<span style="background: #667eea; color: white; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 500;">
|
|
112
|
-
<%= item.class.name %>
|
|
113
|
-
</span>
|
|
114
|
-
</td>
|
|
115
|
-
<td style="padding: 16px;">
|
|
116
|
-
<div style="font-weight: 500; color: #495057; margin-bottom: 4px;">
|
|
117
|
-
<%= link_to truncate(item.recyclable_title, length: 60), recycle_bin.trash_path(item.class.name, item.id),
|
|
118
|
-
style: "color: #667eea; text-decoration: none;" %>
|
|
119
|
-
</div>
|
|
120
|
-
<small style="color: #6c757d;">ID: <%= item.id %></small>
|
|
121
|
-
</td>
|
|
122
|
-
<td style="padding: 16px;">
|
|
123
|
-
<div style="font-weight: 500; color: #495057; margin-bottom: 4px;">
|
|
124
|
-
<%= time_ago_in_words(item.deleted_at) %> ago
|
|
125
|
-
</div>
|
|
126
|
-
<small style="color: #6c757d;"><%= item.deleted_at.strftime('%B %d, %Y at %l:%M %p') %></small>
|
|
127
|
-
</td>
|
|
128
|
-
<td style="padding: 16px;">
|
|
129
|
-
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
|
|
130
|
-
<%= form_with url: recycle_bin.restore_trash_path(item.class.name, item.id), method: :patch, local: true, style: "display: inline;" do |form| %>
|
|
131
|
-
<%= form.submit "↶ Restore",
|
|
132
|
-
style: "display: inline-flex; align-items: center; gap: 4px; padding: 6px 12px; background: #28a745; color: white; border: none; border-radius: 6px; text-decoration: none; font-size: 12px; font-weight: 500; cursor: pointer;",
|
|
133
|
-
onclick: "return confirm('Restore this #{item.class.name.downcase}?')" %>
|
|
134
|
-
<% end %>
|
|
135
|
-
|
|
136
|
-
<%= form_with url: recycle_bin.destroy_trash_path(item.class.name, item.id), method: :delete, local: true, style: "display: inline;" do |form| %>
|
|
137
|
-
<%= form.submit "🗑️ Delete",
|
|
138
|
-
style: "display: inline-flex; align-items: center; gap: 4px; padding: 6px 12px; background: #dc3545; color: white; border: none; border-radius: 6px; text-decoration: none; font-size: 12px; font-weight: 500; cursor: pointer;",
|
|
139
|
-
onclick: "return confirm('Permanently delete this #{item.class.name.downcase}? This cannot be undone!')" %>
|
|
140
|
-
<% end %>
|
|
141
|
-
</div>
|
|
142
|
-
</td>
|
|
82
|
+
<div class="table-container">
|
|
83
|
+
<table class="table">
|
|
84
|
+
<thead>
|
|
85
|
+
<tr>
|
|
86
|
+
<th class="checkbox-column"><input type="checkbox" id="select-all" onchange="toggleSelectAll()"></th>
|
|
87
|
+
<th>Type</th>
|
|
88
|
+
<th>Item</th>
|
|
89
|
+
<th>Deleted At</th>
|
|
90
|
+
<th>Size</th>
|
|
91
|
+
<th>Actions</th>
|
|
143
92
|
</tr>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
93
|
+
</thead>
|
|
94
|
+
<tbody>
|
|
95
|
+
<%= render partial: 'recycle_bin/trash/item', collection: @deleted_items %>
|
|
96
|
+
</tbody>
|
|
97
|
+
</table>
|
|
98
|
+
</div>
|
|
147
99
|
</div>
|
|
148
100
|
|
|
149
|
-
|
|
150
|
-
<% if @total_pages > 1 %>
|
|
151
|
-
<div style="display: flex; justify-content: center; align-items: center; gap: 8px; margin: 30px 0; flex-wrap: wrap;">
|
|
152
|
-
<!-- Previous Button -->
|
|
153
|
-
<% if @current_page > 1 %>
|
|
154
|
-
<%= link_to "« Previous", recycle_bin.root_path(page: @current_page - 1, type: params[:type], time: params[:time]),
|
|
155
|
-
style: "padding: 8px 16px; border: 1px solid #dee2e6; color: #667eea; text-decoration: none; border-radius: 6px; font-weight: 500;" %>
|
|
156
|
-
<% else %>
|
|
157
|
-
<span style="padding: 8px 16px; border: 1px solid #dee2e6; color: #6c757d; border-radius: 6px; background: #f8f9fa;">« Previous</span>
|
|
158
|
-
<% end %>
|
|
159
|
-
|
|
160
|
-
<!-- Page Numbers -->
|
|
161
|
-
<%
|
|
162
|
-
# Calculate page range to show
|
|
163
|
-
start_page = [@current_page - 2, 1].max
|
|
164
|
-
end_page = [@current_page + 2, @total_pages].min
|
|
165
|
-
|
|
166
|
-
# Ensure we show at least 5 pages if available
|
|
167
|
-
if end_page - start_page < 4
|
|
168
|
-
if start_page == 1
|
|
169
|
-
end_page = [start_page + 4, @total_pages].min
|
|
170
|
-
else
|
|
171
|
-
start_page = [end_page - 4, 1].max
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
%>
|
|
175
|
-
|
|
176
|
-
<!-- First page if not in range -->
|
|
177
|
-
<% if start_page > 1 %>
|
|
178
|
-
<%= link_to "1", recycle_bin.root_path(page: 1, type: params[:type], time: params[:time]),
|
|
179
|
-
style: "padding: 8px 12px; border: 1px solid #dee2e6; color: #667eea; text-decoration: none; border-radius: 6px;" %>
|
|
180
|
-
<% if start_page > 2 %>
|
|
181
|
-
<span style="padding: 8px 12px; color: #6c757d;">...</span>
|
|
182
|
-
<% end %>
|
|
183
|
-
<% end %>
|
|
184
|
-
|
|
185
|
-
<!-- Page range -->
|
|
186
|
-
<% (start_page..end_page).each do |page| %>
|
|
187
|
-
<% if page == @current_page %>
|
|
188
|
-
<span style="padding: 8px 12px; background: #667eea; color: white; border-radius: 6px; font-weight: 500;"><%= page %></span>
|
|
189
|
-
<% else %>
|
|
190
|
-
<%= link_to page, recycle_bin.root_path(page: page, type: params[:type], time: params[:time]),
|
|
191
|
-
style: "padding: 8px 12px; border: 1px solid #dee2e6; color: #667eea; text-decoration: none; border-radius: 6px;" %>
|
|
192
|
-
<% end %>
|
|
193
|
-
<% end %>
|
|
194
|
-
|
|
195
|
-
<!-- Last page if not in range -->
|
|
196
|
-
<% if end_page < @total_pages %>
|
|
197
|
-
<% if end_page < @total_pages - 1 %>
|
|
198
|
-
<span style="padding: 8px 12px; color: #6c757d;">...</span>
|
|
199
|
-
<% end %>
|
|
200
|
-
<%= link_to @total_pages, recycle_bin.root_path(page: @total_pages, type: params[:type], time: params[:time]),
|
|
201
|
-
style: "padding: 8px 12px; border: 1px solid #dee2e6; color: #667eea; text-decoration: none; border-radius: 6px;" %>
|
|
202
|
-
<% end %>
|
|
101
|
+
<%= render 'recycle_bin/trash/pagination', current_page: @current_page, total_pages: @total_pages, total_count: @total_count, per_page: @per_page %>
|
|
203
102
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
103
|
+
<% else %>
|
|
104
|
+
<div class="main-card">
|
|
105
|
+
<div class="empty-state">
|
|
106
|
+
<% if params[:search].present? %>
|
|
107
|
+
<div class="empty-icon">🔍</div>
|
|
108
|
+
<h4 class="empty-title">No search results found!</h4>
|
|
109
|
+
<p class="empty-subtitle">
|
|
110
|
+
No items match your search "<strong><%= params[:search] %></strong>".
|
|
111
|
+
Try adjusting your search terms or filters.
|
|
112
|
+
</p>
|
|
113
|
+
<div class="empty-actions">
|
|
114
|
+
<%= link_to "Clear Search", recycle_bin.trash_index_path, class: "btn btn-primary" %>
|
|
115
|
+
<%= link_to "View All Items", recycle_bin.trash_index_path, class: "btn btn-outline" %>
|
|
116
|
+
</div>
|
|
117
|
+
<% elsif any_filters_active? %>
|
|
118
|
+
<div class="empty-icon">📊</div>
|
|
119
|
+
<h4 class="empty-title">No items match your filters!</h4>
|
|
120
|
+
<p class="empty-subtitle">
|
|
121
|
+
Try adjusting your filter criteria or <%= link_to "clear all filters", recycle_bin.trash_index_path %>.
|
|
122
|
+
</p>
|
|
208
123
|
<% else %>
|
|
209
|
-
<
|
|
124
|
+
<div class="empty-icon">🎉</div>
|
|
125
|
+
<h4 class="empty-title">No items match your filters!</h4>
|
|
126
|
+
<p class="empty-subtitle">Try adjusting your filters or check back later for deleted items.</p>
|
|
127
|
+
<%= link_to "Clear Filters", recycle_bin.trash_index_path, class: "btn btn-primary" %>
|
|
210
128
|
<% end %>
|
|
211
129
|
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<% end %>
|
|
212
132
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
<% end %>
|
|
223
|
-
<% end %>
|
|
224
|
-
</div>
|
|
225
|
-
<% end %>
|
|
133
|
+
<style>
|
|
134
|
+
.page-header {
|
|
135
|
+
display: flex;
|
|
136
|
+
justify-content: space-between;
|
|
137
|
+
align-items: center;
|
|
138
|
+
margin-bottom: 24px;
|
|
139
|
+
padding: 20px 0;
|
|
140
|
+
border-bottom: 1px solid #e9ecef;
|
|
141
|
+
}
|
|
226
142
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
143
|
+
.page-title-section {
|
|
144
|
+
flex: 1;
|
|
145
|
+
}
|
|
230
146
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
242
|
-
<% end %>
|
|
147
|
+
.page-title {
|
|
148
|
+
font-size: 28px;
|
|
149
|
+
font-weight: 700;
|
|
150
|
+
color: #495057;
|
|
151
|
+
margin: 0 0 8px 0;
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
gap: 12px;
|
|
155
|
+
}
|
|
243
156
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
itemCheckboxes.forEach(checkbox => {
|
|
251
|
-
checkbox.checked = selectAllCheckbox.checked;
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
updateBulkActions();
|
|
157
|
+
.page-subtitle {
|
|
158
|
+
font-size: 16px;
|
|
159
|
+
color: #6c757d;
|
|
160
|
+
margin: 0;
|
|
161
|
+
line-height: 1.4;
|
|
255
162
|
}
|
|
256
163
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const bulkCount = document.getElementById('bulk-count');
|
|
261
|
-
const selectAllCheckbox = document.getElementById('select-all');
|
|
262
|
-
|
|
263
|
-
if (checkedBoxes.length > 0) {
|
|
264
|
-
bulkActions.style.display = 'flex';
|
|
265
|
-
bulkCount.textContent = checkedBoxes.length + ' item' + (checkedBoxes.length > 1 ? 's' : '') + ' selected';
|
|
266
|
-
} else {
|
|
267
|
-
bulkActions.style.display = 'none';
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Update select-all checkbox state
|
|
271
|
-
const itemCheckboxes = document.querySelectorAll('.item-checkbox');
|
|
272
|
-
if (checkedBoxes.length === itemCheckboxes.length && itemCheckboxes.length > 0) {
|
|
273
|
-
selectAllCheckbox.checked = true;
|
|
274
|
-
selectAllCheckbox.indeterminate = false;
|
|
275
|
-
} else if (checkedBoxes.length > 0) {
|
|
276
|
-
selectAllCheckbox.checked = false;
|
|
277
|
-
selectAllCheckbox.indeterminate = true;
|
|
278
|
-
} else {
|
|
279
|
-
selectAllCheckbox.checked = false;
|
|
280
|
-
selectAllCheckbox.indeterminate = false;
|
|
281
|
-
}
|
|
164
|
+
.page-actions {
|
|
165
|
+
display: flex;
|
|
166
|
+
gap: 12px;
|
|
282
167
|
}
|
|
283
168
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return false;
|
|
169
|
+
@media (max-width: 768px) {
|
|
170
|
+
.page-header {
|
|
171
|
+
flex-direction: column;
|
|
172
|
+
align-items: flex-start;
|
|
173
|
+
gap: 16px;
|
|
290
174
|
}
|
|
291
175
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
let message;
|
|
296
|
-
if (action === 'restore') {
|
|
297
|
-
message = 'Restore ' + checkedBoxes.length + ' selected item' + (checkedBoxes.length > 1 ? 's' : '') + '?';
|
|
298
|
-
} else {
|
|
299
|
-
message = 'Permanently delete ' + checkedBoxes.length + ' selected item' + (checkedBoxes.length > 1 ? 's' : '') + '? This cannot be undone!';
|
|
176
|
+
.page-actions {
|
|
177
|
+
width: 100%;
|
|
178
|
+
justify-content: flex-start;
|
|
300
179
|
}
|
|
301
180
|
|
|
302
|
-
|
|
303
|
-
|
|
181
|
+
.page-title {
|
|
182
|
+
font-size: 24px;
|
|
304
183
|
}
|
|
305
184
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
document.getElementById('bulk-restore-items').value = JSON.stringify(selectedItems);
|
|
309
|
-
} else {
|
|
310
|
-
document.getElementById('bulk-destroy-items').value = JSON.stringify(selectedItems);
|
|
185
|
+
.page-subtitle {
|
|
186
|
+
font-size: 14px;
|
|
311
187
|
}
|
|
312
|
-
|
|
313
|
-
return true;
|
|
314
188
|
}
|
|
315
189
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
190
|
+
.search-summary {
|
|
191
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
192
|
+
color: white;
|
|
193
|
+
padding: 20px;
|
|
194
|
+
border-radius: 8px;
|
|
195
|
+
margin-bottom: 20px;
|
|
196
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.search-summary-content h3 {
|
|
200
|
+
margin: 0 0 10px 0;
|
|
201
|
+
font-size: 1.5rem;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.search-tip {
|
|
205
|
+
margin: 0;
|
|
206
|
+
opacity: 0.9;
|
|
207
|
+
font-size: 0.9rem;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.filter-breadcrumb {
|
|
211
|
+
background: #e3f2fd;
|
|
212
|
+
border: 1px solid #bbdefb;
|
|
213
|
+
border-radius: 6px;
|
|
214
|
+
padding: 12px 16px;
|
|
215
|
+
margin-bottom: 20px;
|
|
216
|
+
display: flex;
|
|
217
|
+
align-items: center;
|
|
218
|
+
gap: 10px;
|
|
219
|
+
flex-wrap: wrap;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.breadcrumb-label {
|
|
223
|
+
font-weight: 600;
|
|
224
|
+
color: #1976d2;
|
|
225
|
+
font-size: 14px;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.breadcrumb-content {
|
|
229
|
+
color: #1565c0;
|
|
230
|
+
font-size: 14px;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.filter-count-badge {
|
|
234
|
+
background: #1976d2;
|
|
235
|
+
color: white;
|
|
236
|
+
padding: 2px 8px;
|
|
237
|
+
border-radius: 12px;
|
|
238
|
+
font-size: 12px;
|
|
239
|
+
font-weight: 600;
|
|
240
|
+
margin-left: auto;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.search-highlight {
|
|
244
|
+
background: #fff3cd;
|
|
245
|
+
color: #856404;
|
|
246
|
+
padding: 2px 8px;
|
|
247
|
+
border-radius: 4px;
|
|
248
|
+
font-size: 0.9rem;
|
|
249
|
+
font-weight: normal;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.export-buttons {
|
|
253
|
+
display: flex;
|
|
254
|
+
gap: 8px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.empty-actions {
|
|
258
|
+
display: flex;
|
|
259
|
+
gap: 10px;
|
|
260
|
+
margin-top: 15px;
|
|
261
|
+
justify-content: center;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.empty-actions .btn {
|
|
265
|
+
min-width: 120px;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@media (max-width: 768px) {
|
|
269
|
+
.search-summary-content h3 {
|
|
270
|
+
font-size: 1.2rem;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.filter-breadcrumb {
|
|
274
|
+
flex-direction: column;
|
|
275
|
+
align-items: flex-start;
|
|
276
|
+
gap: 8px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.filter-count-badge {
|
|
280
|
+
margin-left: 0;
|
|
281
|
+
align-self: flex-start;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.empty-actions {
|
|
285
|
+
flex-direction: column;
|
|
286
|
+
align-items: center;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
</style>
|