infotable 0.1.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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +1043 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/javascripts/infotable/controllers/drawer_controller.js +1412 -0
  6. data/app/assets/javascripts/infotable/controllers/table_controller.js +921 -0
  7. data/app/assets/javascripts/infotable/index.js +14 -0
  8. data/app/controllers/infotable/application_controller.rb +4 -0
  9. data/app/controllers/infotable/tables_controller.rb +299 -0
  10. data/app/helpers/infotable/application_helper.rb +5 -0
  11. data/app/helpers/infotable/tables_helper.rb +354 -0
  12. data/app/jobs/infotable/application_job.rb +4 -0
  13. data/app/mailers/infotable/application_mailer.rb +6 -0
  14. data/app/models/infotable/application_record.rb +5 -0
  15. data/app/views/infotable/tables/_column_visibility.html.erb +100 -0
  16. data/app/views/infotable/tables/_filter_drawer.html.erb +65 -0
  17. data/app/views/infotable/tables/_next_page_frame.html.erb +39 -0
  18. data/app/views/infotable/tables/_pagination.html.erb +101 -0
  19. data/app/views/infotable/tables/_side_panel.html.erb +63 -0
  20. data/app/views/infotable/tables/_table.html.erb +135 -0
  21. data/app/views/infotable/tables/_table_rows.html.erb +38 -0
  22. data/app/views/infotable/tables/filters/_boolean.html.erb +22 -0
  23. data/app/views/infotable/tables/filters/_date.html.erb +30 -0
  24. data/app/views/infotable/tables/filters/_number.html.erb +32 -0
  25. data/app/views/infotable/tables/filters/_select.html.erb +68 -0
  26. data/app/views/infotable/tables/filters/_text.html.erb +38 -0
  27. data/app/views/infotable/tables/show.html.erb +113 -0
  28. data/app/views/layouts/infotable/application.html.erb +15 -0
  29. data/config/importmap.rb +6 -0
  30. data/config/locales/en.yml +88 -0
  31. data/config/locales/pl.yml +88 -0
  32. data/config/routes.rb +6 -0
  33. data/lib/generators/infotable/install/install_generator.rb +565 -0
  34. data/lib/generators/infotable/install/templates/TAILWIND_CONFIG.md +131 -0
  35. data/lib/generators/infotable/install/templates/initializer.rb +26 -0
  36. data/lib/generators/infotable/install/templates/tailwind.config.js +284 -0
  37. data/lib/generators/infotable/table/table_generator.rb +95 -0
  38. data/lib/generators/infotable/table/templates/table.rb.tt +26 -0
  39. data/lib/infotable/base.rb +463 -0
  40. data/lib/infotable/batch_actions.rb +34 -0
  41. data/lib/infotable/configuration.rb +53 -0
  42. data/lib/infotable/engine.rb +51 -0
  43. data/lib/infotable/filter_processor.rb +326 -0
  44. data/lib/infotable/formatters/action_formatter.rb +141 -0
  45. data/lib/infotable/formatters/avatar_formatter.rb +49 -0
  46. data/lib/infotable/formatters/badge_formatter.rb +80 -0
  47. data/lib/infotable/formatters/base_formatter.rb +47 -0
  48. data/lib/infotable/formatters/boolean_formatter.rb +38 -0
  49. data/lib/infotable/formatters/currency_formatter.rb +22 -0
  50. data/lib/infotable/formatters/date_formatter.rb +58 -0
  51. data/lib/infotable/formatters/link_formatter.rb +48 -0
  52. data/lib/infotable/formatters/percent_formatter.rb +27 -0
  53. data/lib/infotable/search_processor.rb +42 -0
  54. data/lib/infotable/table_resolver.rb +45 -0
  55. data/lib/infotable/version.rb +3 -0
  56. data/lib/infotable.rb +54 -0
  57. data/lib/tasks/infotable.rake +55 -0
  58. metadata +216 -0
@@ -0,0 +1,32 @@
1
+ <%# Number filter - from/to range %>
2
+ <div class="grid grid-cols-2 gap-2">
3
+ <%# From input %>
4
+ <div class="relative">
5
+ <input type="number"
6
+ name="filters[<%= column[:name] %>][from]"
7
+ id="<%= infotable_filter_id(table_name, column, 'from') %>"
8
+ placeholder="<%= t('infotable.filters.min') %>"
9
+ step="any"
10
+ class="block w-full rounded-lg border-0 bg-white pr-8 pl-3 py-2 text-base sm:text-sm text-neutral-900 placeholder:text-neutral-500 shadow-sm focus:outline-none focus:ring-2 focus:ring-neutral-600 focus:ring-offset-0 transition-all [box-shadow:inset_0_0_0_1px_rgb(212_212_212)] focus:[box-shadow:inset_0_0_0_2px_rgb(82_82_82)] disabled:cursor-not-allowed disabled:bg-neutral-100">
11
+ <button type="button"
12
+ class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
13
+ data-action="click->infotable--drawer#clearSingleFilter"
14
+ data-filter-name="<%= column[:name] %>"
15
+ data-filter-type="number"
16
+ title="<%= t('infotable.filters.clear_filter') %>">
17
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
18
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
19
+ </svg>
20
+ </button>
21
+ </div>
22
+
23
+ <%# To input %>
24
+ <div class="relative">
25
+ <input type="number"
26
+ name="filters[<%= column[:name] %>][to]"
27
+ id="<%= infotable_filter_id(table_name, column, 'to') %>"
28
+ placeholder="<%= t('infotable.filters.max') %>"
29
+ step="any"
30
+ class="block w-full rounded-lg border-0 bg-white pr-3 pl-3 py-2 text-base sm:text-sm text-neutral-900 placeholder:text-neutral-500 shadow-sm focus:outline-none focus:ring-2 focus:ring-neutral-600 focus:ring-offset-0 transition-all [box-shadow:inset_0_0_0_1px_rgb(212_212_212)] focus:[box-shadow:inset_0_0_0_2px_rgb(82_82_82)] disabled:cursor-not-allowed disabled:bg-neutral-100">
31
+ </div>
32
+ </div>
@@ -0,0 +1,68 @@
1
+ <%# Select filter - dropdown with options (single or multi-select) %>
2
+ <%
3
+ # Options: array or Proc (optionally cached and always capped, see Infotable::TablesHelper#infotable_filter_options)
4
+ options, total_options = infotable_filter_options(local_assigns[:table_class], column)
5
+
6
+ # Check if multi-select is enabled
7
+ is_multi = column[:filter_multi_select] || false
8
+ %>
9
+
10
+ <% if is_multi %>
11
+ <%# Multi-select %>
12
+ <div class="relative">
13
+ <select name="filters[<%= column[:name] %>][]"
14
+ id="<%= infotable_filter_id(table_name, column, 'value') %>"
15
+ multiple
16
+ size="6"
17
+ class="block w-full appearance-none rounded-lg border-0 bg-white pl-3 py-2 pr-8 text-base sm:text-sm text-neutral-900 shadow-sm focus:outline-none focus:ring-2 focus:ring-neutral-600 focus:ring-offset-0 transition-all [box-shadow:inset_0_0_0_1px_rgb(212_212_212)] focus:[box-shadow:inset_0_0_0_2px_rgb(82_82_82)] cursor-pointer disabled:cursor-not-allowed disabled:bg-neutral-100 disabled:opacity-75"
18
+ style="min-height: 120px;">
19
+ <% options.each do |option| %>
20
+ <% if option.is_a?(Array) %>
21
+ <option value="<%= option[1] %>"><%= option[0] %></option>
22
+ <% else %>
23
+ <option value="<%= option %>"><%= option %></option>
24
+ <% end %>
25
+ <% end %>
26
+ </select>
27
+ <button type="button"
28
+ class="absolute right-2 top-2 text-gray-400 hover:text-gray-600 transition-colors"
29
+ data-action="click->infotable--drawer#clearSingleFilter"
30
+ data-filter-name="<%= column[:name] %>"
31
+ data-filter-type="select"
32
+ title="<%= t('infotable.filters.clear_filter') %>">
33
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
34
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
35
+ </svg>
36
+ </button>
37
+ </div>
38
+ <p class="mt-1.5 text-xs text-neutral-500"><%= t('infotable.filters.multi_select_hint') %></p>
39
+ <% else %>
40
+ <%# Single select %>
41
+ <div class="relative">
42
+ <select name="filters[<%= column[:name] %>][value]"
43
+ id="<%= infotable_filter_id(table_name, column, 'value') %>"
44
+ class="block w-full appearance-none rounded-lg border-0 bg-white pr-10 pl-3 py-2 text-base sm:text-sm text-neutral-900 shadow-sm focus:outline-none focus:ring-2 focus:ring-neutral-600 focus:ring-offset-0 transition-all [box-shadow:inset_0_0_0_1px_rgb(212_212_212)] focus:[box-shadow:inset_0_0_0_2px_rgb(82_82_82)] cursor-pointer disabled:cursor-not-allowed disabled:bg-neutral-100 disabled:opacity-75 bg-[url('data:image/svg+xml,%3csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20fill=%27none%27%20viewBox=%270%200%2020%2020%27%3e%3cpath%20stroke=%27%23737373%27%20stroke-linecap=%27round%27%20stroke-linejoin=%27round%27%20stroke-width=%271.5%27%20d=%27M6%208l4%204%204-4%27/%3e%3c/svg%3e')] bg-[right_0.75rem_center] bg-no-repeat bg-[length:1.25em_1.25em]">
45
+ <option value=""><%= t('infotable.filters.all') %></option>
46
+ <% options.each do |option| %>
47
+ <% if option.is_a?(Array) %>
48
+ <option value="<%= option[1] %>"><%= option[0] %></option>
49
+ <% else %>
50
+ <option value="<%= option %>"><%= option %></option>
51
+ <% end %>
52
+ <% end %>
53
+ </select>
54
+ <button type="button"
55
+ class="absolute right-8 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
56
+ data-action="click->infotable--drawer#clearSingleFilter"
57
+ data-filter-name="<%= column[:name] %>"
58
+ data-filter-type="select"
59
+ title="<%= t('infotable.filters.clear_filter') %>">
60
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
61
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
62
+ </svg>
63
+ </button>
64
+ </div>
65
+ <% end %>
66
+ <% if total_options > options.size %>
67
+ <p class="mt-1.5 text-xs text-amber-600" data-filter-options-truncated><%= t('infotable.filters.options_truncated', shown: options.size, total: total_options) %></p>
68
+ <% end %>
@@ -0,0 +1,38 @@
1
+ <%# Text/String filter with operation dropdown %>
2
+ <div class="grid grid-cols-[1fr_2fr] gap-2" data-filter-container id="<%= infotable_filter_id(table_name, column, 'container') %>">
3
+ <div class="relative">
4
+ <%# Operation selector %>
5
+ <select name="filters[<%= column[:name] %>][op]"
6
+ data-filter-op
7
+ data-action="change->infotable--drawer#syncFilterOps"
8
+ id="<%= infotable_filter_id(table_name, column, 'op') %>"
9
+ class="block w-full appearance-none rounded-lg border-0 bg-white pr-10 pl-3 py-2 text-base sm:text-sm text-neutral-900 shadow-sm focus:outline-none focus:ring-2 focus:ring-neutral-600 focus:ring-offset-0 transition-all [box-shadow:inset_0_0_0_1px_rgb(212_212_212)] focus:[box-shadow:inset_0_0_0_2px_rgb(82_82_82)] cursor-pointer disabled:cursor-not-allowed disabled:bg-neutral-100 disabled:opacity-75 bg-[url('data:image/svg+xml,%3csvg%20xmlns=%27http://www.w3.org/2000/svg%27%20fill=%27none%27%20viewBox=%270%200%2020%2020%27%3e%3cpath%20stroke=%27%23737373%27%20stroke-linecap=%27round%27%20stroke-linejoin=%27round%27%20stroke-width=%271.5%27%20d=%27M6%208l4%204%204-4%27/%3e%3c/svg%3e')] bg-[right_0.75rem_center] bg-no-repeat bg-[length:1.25em_1.25em]">
10
+ <option value="contains"><%= t('infotable.filters.text_ops.contains') %></option>
11
+ <option value="exact"><%= t('infotable.filters.text_ops.exact') %></option>
12
+ <option value="not_contain"><%= t('infotable.filters.text_ops.not_contain') %></option>
13
+ <option value="starts_with"><%= t('infotable.filters.text_ops.starts_with') %></option>
14
+ <option value="ends_with"><%= t('infotable.filters.text_ops.ends_with') %></option>
15
+ <option value="is_empty"><%= t('infotable.filters.text_ops.is_empty') %></option>
16
+ <option value="is_not_empty"><%= t('infotable.filters.text_ops.is_not_empty') %></option>
17
+ </select>
18
+ </div>
19
+
20
+ <%# Value input - hidden for is_empty/is_not_empty %>
21
+ <div data-filter-value-wrapper id="<%= infotable_filter_id(table_name, column, 'value_wrapper') %>" class="relative">
22
+ <input type="text"
23
+ name="filters[<%= column[:name] %>][value]"
24
+ id="<%= infotable_filter_id(table_name, column, 'value') %>"
25
+ placeholder="<%= t('infotable.filters.enter_value') %>"
26
+ class="block w-full rounded-lg border-0 bg-white pr-8 pl-3 py-2 text-base sm:text-sm text-neutral-900 placeholder:text-neutral-500 shadow-sm focus:outline-none focus:ring-2 focus:ring-neutral-600 focus:ring-offset-0 transition-all [box-shadow:inset_0_0_0_1px_rgb(212_212_212)] focus:[box-shadow:inset_0_0_0_2px_rgb(82_82_82)] disabled:cursor-not-allowed disabled:bg-neutral-100">
27
+ <button type="button"
28
+ class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors"
29
+ data-action="click->infotable--drawer#clearSingleFilter"
30
+ data-filter-name="<%= column[:name] %>"
31
+ data-filter-type="text"
32
+ title="<%= t('infotable.filters.clear_filter') %>">
33
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
34
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
35
+ </svg>
36
+ </button>
37
+ </div>
38
+ </div>
@@ -0,0 +1,113 @@
1
+ <%# This view renders inside the turbo frame %>
2
+ <%# Turbo requires the frame element to be present in the response %>
3
+ <turbo-frame id="<%= params[:table_name] %>_content">
4
+ <div class="relative overflow-y-auto overflow-x-auto scroll-smooth [scrollbar-width:thin] [scrollbar-color:#cbd5e1_#f1f5f9] [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar]:h-2 [&::-webkit-scrollbar-track]:bg-slate-100 [&::-webkit-scrollbar-thumb]:bg-slate-300 [&::-webkit-scrollbar-thumb]:rounded [&::-webkit-scrollbar-thumb:hover]:bg-slate-400"
5
+ style="min-height: <%= @table_class.min_height %>; max-height: <%= @table_class.max_height %>;">
6
+
7
+ <% if @records.any? %>
8
+ <%# Table %>
9
+ <table class="min-w-full divide-y divide-gray-200">
10
+ <thead class="bg-gray-50 border-b-2 border-gray-200">
11
+ <tr>
12
+ <%
13
+ row_size = @table_class.row_size_value
14
+ header_padding = case row_size
15
+ when :slim then "px-2 py-1"
16
+ when :mid then "px-4 py-2"
17
+ else "px-6 py-3.5" # default
18
+ end
19
+ %>
20
+ <% if @table_class.checkbox_enabled? %>
21
+ <th scope="col" class="<%= header_padding %> w-10 bg-gray-50 sticky top-0 z-20">
22
+ <input type="checkbox"
23
+ aria-label="<%= t('infotable.batch.select_all') %>"
24
+ data-infotable--table-target="selectAll"
25
+ data-action="change->infotable--table#toggleAll"
26
+ class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500">
27
+ </th>
28
+ <% end %>
29
+ <% @table_class.columns.each do |column| %>
30
+ <th scope="col"
31
+ data-column-name="<%= column[:name] %>"
32
+ data-default-visible="<%= column[:visible] %>"
33
+ <%= tag.attributes(data: { default_pinned: column[:pinned] }) if column[:pinned] %>
34
+ style="width: <%= column[:width] %>; max-width: <%= column[:width] %>; overflow: hidden;"
35
+ class="<%= header_padding %> text-left text-xs font-semibold text-gray-900 tracking-wider select-none bg-gray-50 sticky top-0 z-20">
36
+ <% if column[:sortable] %>
37
+ <% sort_index = current_sorts.index { |name, _| name == column[:name].to_s } %>
38
+ <% sort_dir = sort_index ? current_sorts[sort_index].last : nil %>
39
+ <% new_direction = sort_dir == 'asc' ? 'desc' : 'asc' %>
40
+ <%# Plain click: sort by this column only. Shift-click (JS) adds it to the sort list. %>
41
+ <%= link_to infotable_table_data_path(
42
+ table_url_params(
43
+ sort_column: column[:name],
44
+ sort_direction: new_direction,
45
+ sort: nil,
46
+ page: 1 # Reset to page 1 when sorting changes
47
+ )
48
+ ),
49
+ title: t('infotable.table.sort_hint'),
50
+ aria: { sort: sort_dir && (sort_dir == 'asc' ? 'ascending' : 'descending') },
51
+ class: "group inline-flex items-center gap-2 hover:text-indigo-600 transition-colors",
52
+ data: { turbo_frame: "#{params[:table_name]}_content",
53
+ action: "click->infotable--table#sortClick",
54
+ sort_column: column[:name] } do %>
55
+ <span><%= column[:label] %></span>
56
+ <span class="flex-none rounded">
57
+ <% if sort_index %>
58
+ <% if sort_dir == 'asc' %>
59
+ <svg class="h-4 w-4 text-indigo-600" fill="currentColor" viewBox="0 0 20 20">
60
+ <path fill-rule="evenodd" d="M14.707 12.707a1 1 0 01-1.414 0L10 9.414l-3.293 3.293a1 1 0 01-1.414-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 010 1.414z" clip-rule="evenodd"/>
61
+ </svg>
62
+ <% else %>
63
+ <svg class="h-4 w-4 text-indigo-600" fill="currentColor" viewBox="0 0 20 20">
64
+ <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
65
+ </svg>
66
+ <% end %>
67
+ <% if current_sorts.size > 1 %>
68
+ <span class="text-[10px] font-bold text-indigo-600"><%= sort_index + 1 %></span>
69
+ <% end %>
70
+ <% else %>
71
+ <svg class="h-4 w-4 text-gray-400 opacity-0 group-hover:opacity-100 transition-opacity" fill="currentColor" viewBox="0 0 20 20">
72
+ <path d="M5 12a1 1 0 102 0V6.414l1.293 1.293a1 1 0 001.414-1.414l-3-3a1 1 0 00-1.414 0l-3 3a1 1 0 001.414 1.414L5 6.414V12zM15 8a1 1 0 10-2 0v5.586l-1.293-1.293a1 1 0 00-1.414 1.414l3 3a1 1 0 001.414 0l3-3a1 1 0 00-1.414-1.414L15 13.586V8z"/>
73
+ </svg>
74
+ <% end %>
75
+ </span>
76
+ <% end %>
77
+ <% else %>
78
+ <%= column[:label] %>
79
+ <% end %>
80
+ </th>
81
+ <% end %>
82
+ </tr>
83
+ </thead>
84
+ <tbody id="<%= params[:table_name] %>_tbody" class="bg-white">
85
+ <%= render partial: "infotable/tables/table_rows", locals: { records: @records, table_class: @table_class } %>
86
+ </tbody>
87
+ </table>
88
+
89
+ <%# Next page frame for infinite scroll - rendered after table %>
90
+ <% if @infinite_scroll_enabled && params[:page].blank? %>
91
+ <%= render partial: "infotable/tables/next_page_frame" %>
92
+ <% end %>
93
+ <% else %>
94
+ <%# Empty state %>
95
+ <div class="text-center py-16 px-6">
96
+ <svg class="mx-auto h-16 w-16 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
97
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"/>
98
+ </svg>
99
+ <h3 class="mt-4 text-lg font-medium text-gray-900"><%= t('infotable.table.empty_state.title') %></h3>
100
+ <p class="mt-2 text-sm text-gray-500"><%= t('infotable.table.empty_state.description') %></p>
101
+ </div>
102
+ <% end %>
103
+
104
+ </div>
105
+
106
+ <%# Pagination - Only visible when infinite scroll is disabled %>
107
+ <% unless @infinite_scroll_enabled %>
108
+ <div class="border-t border-gray-200 bg-white px-6 py-4">
109
+ <%= render "infotable/tables/pagination", pagy: @pagy, table_name: params[:table_name] %>
110
+ </div>
111
+ <% end %>
112
+
113
+ </turbo-frame>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Infotable</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
@@ -0,0 +1,6 @@
1
+ # Pin infotable JavaScript modules
2
+ pin "infotable", to: "infotable/index.js"
3
+ pin_all_from Infotable::Engine.root.join("app/assets/javascripts/infotable/controllers"), under: "controllers/infotable", to: "infotable/controllers"
4
+
5
+ # External dependencies
6
+ pin "sortablejs", to: "https://cdn.jsdelivr.net/npm/sortablejs@1.15.7/modular/sortable.esm.js"
@@ -0,0 +1,88 @@
1
+ en:
2
+ infotable:
3
+ table:
4
+ buttons:
5
+ export_csv: "Export CSV"
6
+ filters: "Filters"
7
+ exporting: "Exporting..."
8
+ sort_hint: "Click to sort, Shift+click to add a column to the sort"
9
+ loading:
10
+ initial: "Loading data..."
11
+ more_items: "Loading more items..."
12
+ all_loaded: "All items loaded"
13
+ empty_state:
14
+ title: "No data found"
15
+ description: "Get started by adding some records to see them here."
16
+
17
+ drawer:
18
+ label: "Filters and columns"
19
+ panel_title:
20
+ customize_columns: "Customize Columns"
21
+ filters: "Filters"
22
+ columns: "Columns"
23
+ tabs:
24
+ filters: "Filters"
25
+ columns: "Columns"
26
+
27
+ columns:
28
+ instructions: "Drag to reorder, pin columns, and show/hide:"
29
+ pin:
30
+ left: "Pin to left"
31
+ right: "Pin to right"
32
+ actions:
33
+ select_all: "Select All"
34
+ deselect_all: "Deselect All"
35
+ reset_to_defaults: "🔄 Reset to Defaults"
36
+
37
+ filters:
38
+ actions:
39
+ clear_all: "Clear All"
40
+ apply_filters: "Apply Filters"
41
+ clear_filter: "Clear this filter"
42
+ from: "From"
43
+ to: "To"
44
+ min: "Min"
45
+ max: "Max"
46
+ enter_value: "Enter value..."
47
+ all: "All"
48
+ multi_select_hint: "Hold Ctrl/Cmd to select multiple"
49
+ options_truncated: "Showing the first %{shown} of %{total} options"
50
+ boolean:
51
+ "true": "True"
52
+ "false": "False"
53
+ empty: "Empty"
54
+ not_empty: "Not empty"
55
+ text_ops:
56
+ contains: "Contains"
57
+ exact: "Exact match"
58
+ not_contain: "Does not contain"
59
+ starts_with: "Starts with"
60
+ ends_with: "Ends with"
61
+ is_empty: "Is empty"
62
+ is_not_empty: "Is not empty"
63
+
64
+ formatters:
65
+ ago: "%{time} ago"
66
+ action: "Action"
67
+ boolean:
68
+ "true": "Yes"
69
+ "false": "No"
70
+
71
+ batch:
72
+ selected: "%{count} selected"
73
+ clear: "Clear selection"
74
+ select_all: "Select all rows"
75
+ select_row: "Select row"
76
+
77
+ search:
78
+ placeholder: "Search..."
79
+ label: "Search table"
80
+
81
+ pagination:
82
+ showing: "Showing"
83
+ to: "to"
84
+ of: "of"
85
+ results: "results"
86
+ rows_per_page: "Rows per page:"
87
+ previous: "Previous"
88
+ next: "Next"
@@ -0,0 +1,88 @@
1
+ pl:
2
+ infotable:
3
+ table:
4
+ buttons:
5
+ export_csv: "Eksportuj CSV"
6
+ filters: "Filtry"
7
+ exporting: "Eksportowanie..."
8
+ sort_hint: "Kliknij, aby sortować; Shift+klik dodaje kolumnę do sortowania"
9
+ loading:
10
+ initial: "Ładowanie danych..."
11
+ more_items: "Ładowanie kolejnych elementów..."
12
+ all_loaded: "Wszystkie elementy załadowane"
13
+ empty_state:
14
+ title: "Nie znaleziono danych"
15
+ description: "Rozpocznij od dodania rekordów, aby zobaczyć je tutaj."
16
+
17
+ drawer:
18
+ label: "Filtry i kolumny"
19
+ panel_title:
20
+ customize_columns: "Dostosuj kolumny"
21
+ filters: "Filtry"
22
+ columns: "Kolumny"
23
+ tabs:
24
+ filters: "Filtry"
25
+ columns: "Kolumny"
26
+
27
+ columns:
28
+ instructions: "Przeciągnij, aby zmienić kolejność, przypnij kolumny i pokaż/ukryj:"
29
+ pin:
30
+ left: "Przypnij do lewej"
31
+ right: "Przypnij do prawej"
32
+ actions:
33
+ select_all: "Zaznacz wszystkie"
34
+ deselect_all: "Odznacz wszystkie"
35
+ reset_to_defaults: "🔄 Przywróć domyślne"
36
+
37
+ filters:
38
+ actions:
39
+ clear_all: "Wyczyść wszystkie"
40
+ apply_filters: "Zastosuj filtry"
41
+ clear_filter: "Wyczyść ten filtr"
42
+ from: "Od"
43
+ to: "Do"
44
+ min: "Min"
45
+ max: "Maks"
46
+ enter_value: "Wpisz wartość..."
47
+ all: "Wszystkie"
48
+ multi_select_hint: "Przytrzymaj Ctrl/Cmd, aby wybrać wiele"
49
+ options_truncated: "Pokazano pierwsze %{shown} z %{total} opcji"
50
+ boolean:
51
+ "true": "Tak"
52
+ "false": "Nie"
53
+ empty: "Puste"
54
+ not_empty: "Niepuste"
55
+ text_ops:
56
+ contains: "Zawiera"
57
+ exact: "Dokładnie"
58
+ not_contain: "Nie zawiera"
59
+ starts_with: "Zaczyna się od"
60
+ ends_with: "Kończy się na"
61
+ is_empty: "Jest puste"
62
+ is_not_empty: "Nie jest puste"
63
+
64
+ formatters:
65
+ ago: "%{time} temu"
66
+ action: "Akcja"
67
+ boolean:
68
+ "true": "Tak"
69
+ "false": "Nie"
70
+
71
+ batch:
72
+ selected: "Zaznaczono: %{count}"
73
+ clear: "Wyczyść zaznaczenie"
74
+ select_all: "Zaznacz wszystkie wiersze"
75
+ select_row: "Zaznacz wiersz"
76
+
77
+ search:
78
+ placeholder: "Szukaj..."
79
+ label: "Szukaj w tabeli"
80
+
81
+ pagination:
82
+ showing: "Wyświetlanie"
83
+ to: "do"
84
+ of: "z"
85
+ results: "wyników"
86
+ rows_per_page: "Wierszy na stronę:"
87
+ previous: "Poprzednia"
88
+ next: "Następna"
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ Infotable::Engine.routes.draw do
2
+ # Main table routes
3
+ get "tables/:table_name", to: "tables#show", as: :table
4
+ get "tables/:table_name/data", to: "tables#data", as: :table_data
5
+ get "tables/:table_name/export", to: "tables#export", as: :table_export
6
+ end