recycle_bin 1.1.0 → 1.1.1

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.
@@ -1,320 +1,60 @@
1
- <h2>Deleted Items (<%= number_with_delimiter(@total_count) %>)</h2>
2
-
3
- <!-- Statistics Dashboard -->
4
- <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px;">
5
- <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;">
6
- <div style="font-size: 2rem; font-weight: bold; color: #667eea; margin-bottom: 8px;"><%= number_with_delimiter(@total_count) %></div>
7
- <div style="color: #6c757d; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.5px;">Total Items in Trash</div>
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>
23
- </div>
24
-
25
- <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;">
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>
41
- </div>
42
- </div>
43
-
44
- <!-- Filters -->
45
- <% if @model_types.any? %>
46
- <div style="background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 20px;">
47
- <div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
48
- <span style="font-weight: 500; color: #495057;">Filter by type:</span>
49
- <%= link_to "All", recycle_bin.root_path(page: 1),
50
- 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].blank? ? 'background: #667eea; color: white;' : 'color: #495057;'}" %>
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;'}" %>
54
- <% end %>
55
- </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
- </div>
67
- <% end %>
68
-
1
+ <% content_for :title, "Deleted Items" %>
2
+ <%= render 'recycle_bin/trash/stats',
3
+ total_count: @total_count,
4
+ model_types: @model_types,
5
+ filtered_items: @filtered_items,
6
+ current_page: @current_page,
7
+ per_page: @per_page,
8
+ total_pages: @total_pages %>
9
+ <%= render 'recycle_bin/trash/filters', model_types: @model_types %>
69
10
  <% if @deleted_items.any? %>
70
- <div style="background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); overflow: hidden;">
71
-
72
- <!-- Bulk Actions Bar (Initially Hidden) -->
73
- <div id="bulk-actions" style="display: none; background: #fff3cd; padding: 16px 20px; border-bottom: 1px solid #ffeaa7; justify-content: space-between; align-items: center;">
74
- <span id="bulk-count" style="font-weight: 500; color: #856404;">0 items selected</span>
75
- <div style="display: flex; gap: 8px;">
76
- <%= form_with url: recycle_bin.bulk_restore_trash_index_path, method: :patch, local: true, style: "display: inline;" do |form| %>
11
+ <div class="main-card">
12
+ <div class="card-header">
13
+ <h3 class="card-title">Deleted Items (<%= number_with_delimiter(@total_count) %>)</h3>
14
+ <div class="card-actions">
15
+ <%# <label class="btn btn-outline btn-sm">
16
+ <input type="checkbox" id="auto-refresh"> Auto-refresh
17
+ </label> %>
18
+ </div>
19
+ </div>
20
+ <div id="bulk-actions" class="bulk-actions">
21
+ <span id="bulk-count">0 items selected</span>
22
+ <div class="card-actions">
23
+ <%= form_with url: recycle_bin.bulk_restore_trash_index_path, method: :patch, local: true do |form| %>
77
24
  <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')" %>
25
+ <%= form.submit "↶ Restore Selected", class: "btn btn-success btn-sm", onclick: "return handleBulkAction('restore')" %>
81
26
  <% end %>
82
-
83
- <%= form_with url: recycle_bin.bulk_destroy_trash_index_path, method: :delete, local: true, style: "display: inline;" do |form| %>
27
+ <%= form_with url: recycle_bin.bulk_destroy_trash_index_path, method: :delete, local: true do |form| %>
84
28
  <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')" %>
29
+ <%= form.submit "🗑️ Delete Selected", class: "btn btn-danger btn-sm", onclick: "return handleBulkAction('destroy')" %>
88
30
  <% end %>
89
31
  </div>
90
32
  </div>
91
-
92
- <table style="width: 100%; border-collapse: collapse;">
93
- <thead>
94
- <tr style="background: #f8f9fa; border-bottom: 2px solid #dee2e6;">
95
- <th style="padding: 16px; text-align: left; font-weight: 600; color: #495057; width: 40px;">
96
- <input type="checkbox" id="select-all" style="cursor: pointer;" onchange="toggleSelectAll()">
97
- </th>
98
- <th style="padding: 16px; text-align: left; font-weight: 600; color: #495057;">Type</th>
99
- <th style="padding: 16px; text-align: left; font-weight: 600; color: #495057;">Item</th>
100
- <th style="padding: 16px; text-align: left; font-weight: 600; color: #495057;">Deleted At</th>
101
- <th style="padding: 16px; text-align: left; font-weight: 600; color: #495057;">Actions</th>
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>
33
+ <div class="table-container">
34
+ <table class="table">
35
+ <thead>
36
+ <tr>
37
+ <th class="checkbox-column"><input type="checkbox" id="select-all" onchange="toggleSelectAll()"></th>
38
+ <th>Type</th>
39
+ <th>Item</th>
40
+ <th>Deleted At</th>
41
+ <th>Actions</th>
143
42
  </tr>
144
- <% end %>
145
- </tbody>
146
- </table>
147
- </div>
148
-
149
- <!-- Pagination -->
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 %>
203
-
204
- <!-- Next Button -->
205
- <% if @current_page < @total_pages %>
206
- <%= link_to "Next »", recycle_bin.root_path(page: @current_page + 1, type: params[:type], time: params[:time]),
207
- style: "padding: 8px 16px; border: 1px solid #dee2e6; color: #667eea; text-decoration: none; border-radius: 6px; font-weight: 500;" %>
208
- <% else %>
209
- <span style="padding: 8px 16px; border: 1px solid #dee2e6; color: #6c757d; border-radius: 6px; background: #f8f9fa;">Next »</span>
210
- <% end %>
211
- </div>
212
-
213
- <!-- Per Page Options -->
214
- <div style="text-align: center; margin-bottom: 20px;">
215
- <span style="color: #6c757d; margin-right: 10px;">Items per page:</span>
216
- <% [25, 50, 100, 250].each do |per_page_option| %>
217
- <% if per_page_option == @per_page %>
218
- <span style="padding: 4px 8px; background: #667eea; color: white; border-radius: 4px; margin: 0 2px; font-size: 14px;"><%= per_page_option %></span>
219
- <% else %>
220
- <%= link_to per_page_option, recycle_bin.root_path(page: 1, per_page: per_page_option, type: params[:type], time: params[:time]),
221
- style: "padding: 4px 8px; border: 1px solid #dee2e6; color: #667eea; text-decoration: none; border-radius: 4px; margin: 0 2px; font-size: 14px;" %>
222
- <% end %>
223
- <% end %>
43
+ </thead>
44
+ <tbody>
45
+ <%= render partial: 'recycle_bin/trash/item', collection: @deleted_items %>
46
+ </tbody>
47
+ </table>
224
48
  </div>
225
- <% end %>
226
-
227
- <div style="text-align: center; padding: 20px; color: #6c757d;">
228
- Showing <%= (@current_page - 1) * @per_page + 1 %>-<%= [(@current_page * @per_page), @total_count].min %> of <%= number_with_delimiter(@total_count) %> items
229
49
  </div>
230
-
50
+ <%= render 'recycle_bin/trash/pagination', current_page: @current_page, total_pages: @total_pages, total_count: @total_count, per_page: @per_page %>
231
51
  <% else %>
232
- <div class="empty-state">
233
- <div style="font-size: 48px; margin-bottom: 20px;">🎉</div>
234
- <h3>No items match your filters!</h3>
235
- <p>Try adjusting your filters or check back later for deleted items.</p>
236
-
237
- <div style="margin-top: 30px;">
238
- <%= link_to "Clear Filters", recycle_bin.root_path,
239
- style: "display: inline-flex; align-items: center; gap: 8px; padding: 12px 24px; background: #667eea; color: white; border-radius: 6px; text-decoration: none; font-weight: 500;" %>
52
+ <div class="main-card">
53
+ <div class="empty-state">
54
+ <div class="empty-icon">🎉</div>
55
+ <h4 class="empty-title">No items match your filters!</h4>
56
+ <p class="empty-subtitle">Try adjusting your filters or check back later for deleted items.</p>
57
+ <%= link_to "Clear Filters", recycle_bin.root_path, class: "btn btn-primary" %>
240
58
  </div>
241
59
  </div>
242
- <% end %>
243
-
244
- <script>
245
- // Bulk selection functionality
246
- function toggleSelectAll() {
247
- const selectAllCheckbox = document.getElementById('select-all');
248
- const itemCheckboxes = document.querySelectorAll('.item-checkbox');
249
-
250
- itemCheckboxes.forEach(checkbox => {
251
- checkbox.checked = selectAllCheckbox.checked;
252
- });
253
-
254
- updateBulkActions();
255
- }
256
-
257
- function updateBulkActions() {
258
- const checkedBoxes = document.querySelectorAll('.item-checkbox:checked');
259
- const bulkActions = document.getElementById('bulk-actions');
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
- }
282
- }
283
-
284
- function handleBulkAction(action) {
285
- const checkedBoxes = document.querySelectorAll('.item-checkbox:checked');
286
-
287
- if (checkedBoxes.length === 0) {
288
- alert('Please select at least one item.');
289
- return false;
290
- }
291
-
292
- const selectedItems = Array.from(checkedBoxes).map(cb => cb.value);
293
-
294
- // Confirmation message
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!';
300
- }
301
-
302
- if (!confirm(message)) {
303
- return false;
304
- }
305
-
306
- // Set the hidden input values
307
- if (action === 'restore') {
308
- document.getElementById('bulk-restore-items').value = JSON.stringify(selectedItems);
309
- } else {
310
- document.getElementById('bulk-destroy-items').value = JSON.stringify(selectedItems);
311
- }
312
-
313
- return true;
314
- }
315
-
316
- // Initialize bulk actions on page load
317
- document.addEventListener('DOMContentLoaded', function() {
318
- updateBulkActions();
319
- });
320
- </script>
60
+ <% end %>