smriti 0.5.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 (124) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +168 -0
  4. data/Rakefile +15 -0
  5. data/app/assets/images/smriti/android-chrome-192x192.png +0 -0
  6. data/app/assets/images/smriti/android-chrome-512x512.png +0 -0
  7. data/app/assets/images/smriti/apple-touch-icon.png +0 -0
  8. data/app/assets/images/smriti/favicon-16x16.png +0 -0
  9. data/app/assets/images/smriti/favicon-32x32.png +0 -0
  10. data/app/assets/images/smriti/favicon-48x48.png +0 -0
  11. data/app/assets/images/smriti/favicon.ico +0 -0
  12. data/app/assets/images/smriti/favicon.svg +18 -0
  13. data/app/assets/images/smriti/logo.svg +18 -0
  14. data/app/assets/images/smriti/mask-icon.svg +5 -0
  15. data/app/assets/stylesheets/smriti/application.css +1040 -0
  16. data/app/controllers/smriti/admin/application_controller.rb +135 -0
  17. data/app/controllers/smriti/admin/dashboard_controller.rb +32 -0
  18. data/app/controllers/smriti/admin/mat_view_definitions_controller.rb +372 -0
  19. data/app/controllers/smriti/admin/mat_view_runs_controller.rb +185 -0
  20. data/app/controllers/smriti/admin/preferences_controller.rb +91 -0
  21. data/app/helpers/smriti/admin/datatable_helper.rb +249 -0
  22. data/app/helpers/smriti/admin/localized_digit_helper.rb +70 -0
  23. data/app/helpers/smriti/admin/ui_helper.rb +539 -0
  24. data/app/javascript/smriti/application.js +8 -0
  25. data/app/javascript/smriti/controllers/application.js +10 -0
  26. data/app/javascript/smriti/controllers/body_setup_controller.js +120 -0
  27. data/app/javascript/smriti/controllers/datatable_controller.js +351 -0
  28. data/app/javascript/smriti/controllers/details_controller.js +200 -0
  29. data/app/javascript/smriti/controllers/drawer_controller.js +470 -0
  30. data/app/javascript/smriti/controllers/flash_controller.js +112 -0
  31. data/app/javascript/smriti/controllers/index.js +10 -0
  32. data/app/javascript/smriti/controllers/mv_confirm_controller.js +435 -0
  33. data/app/javascript/smriti/controllers/tabs_controller.js +184 -0
  34. data/app/javascript/smriti/controllers/tooltip_controller.js +525 -0
  35. data/app/javascript/smriti/controllers/turbo_frame_lifecycle_controller.js +342 -0
  36. data/app/jobs/smriti/application_job.rb +144 -0
  37. data/app/jobs/smriti/create_view_job.rb +87 -0
  38. data/app/jobs/smriti/delete_view_job.rb +89 -0
  39. data/app/jobs/smriti/refresh_view_job.rb +94 -0
  40. data/app/models/concerns/smriti_i18n.rb +139 -0
  41. data/app/models/concerns/smriti_paginate.rb +70 -0
  42. data/app/models/concerns/smriti_query_helper.rb +36 -0
  43. data/app/models/smriti/application_record.rb +39 -0
  44. data/app/models/smriti/mat_view_definition.rb +254 -0
  45. data/app/models/smriti/mat_view_run.rb +275 -0
  46. data/app/views/layouts/smriti/_footer.html.erb +47 -0
  47. data/app/views/layouts/smriti/_header.html.erb +25 -0
  48. data/app/views/layouts/smriti/admin.html.erb +47 -0
  49. data/app/views/layouts/smriti/turbo_frame.html.erb +3 -0
  50. data/app/views/smriti/admin/dashboard/index.html.erb +38 -0
  51. data/app/views/smriti/admin/mat_view_definitions/_definition_actions.html.erb +94 -0
  52. data/app/views/smriti/admin/mat_view_definitions/_dt-index-empty-row.html.erb +11 -0
  53. data/app/views/smriti/admin/mat_view_definitions/_dt-index-row.html.erb +27 -0
  54. data/app/views/smriti/admin/mat_view_definitions/empty.html.erb +1 -0
  55. data/app/views/smriti/admin/mat_view_definitions/form.html.erb +79 -0
  56. data/app/views/smriti/admin/mat_view_definitions/index.html.erb +10 -0
  57. data/app/views/smriti/admin/mat_view_definitions/show.html.erb +40 -0
  58. data/app/views/smriti/admin/mat_view_runs/_dt-index-empty-row.html.erb +11 -0
  59. data/app/views/smriti/admin/mat_view_runs/_dt-index-row.html.erb +41 -0
  60. data/app/views/smriti/admin/mat_view_runs/index.html.erb +1 -0
  61. data/app/views/smriti/admin/mat_view_runs/show.html.erb +64 -0
  62. data/app/views/smriti/admin/preferences/show.html.erb +49 -0
  63. data/app/views/smriti/admin/ui/_card.html.erb +15 -0
  64. data/app/views/smriti/admin/ui/_datatable.html.erb +34 -0
  65. data/app/views/smriti/admin/ui/_datatable_filters.html.erb +45 -0
  66. data/app/views/smriti/admin/ui/_datatable_tbody.html.erb +11 -0
  67. data/app/views/smriti/admin/ui/_datatable_tfoot.html.erb +70 -0
  68. data/app/views/smriti/admin/ui/_datatable_thead.html.erb +105 -0
  69. data/app/views/smriti/admin/ui/_details.html.erb +10 -0
  70. data/app/views/smriti/admin/ui/_flash.html.erb +6 -0
  71. data/app/views/smriti/admin/ui/_table.html.erb +8 -0
  72. data/config/importmap.rb +9 -0
  73. data/config/locales/ar.yml +223 -0
  74. data/config/locales/de.yml +230 -0
  75. data/config/locales/en-AU-ocker.yml +223 -0
  76. data/config/locales/en-AU.yml +202 -0
  77. data/config/locales/en-BORK.yml +225 -0
  78. data/config/locales/en-CA.yml +223 -0
  79. data/config/locales/en-GB.yml +223 -0
  80. data/config/locales/en-LOL.yml +219 -0
  81. data/config/locales/en-SCOT.yml +223 -0
  82. data/config/locales/en-SHAKESPEARE.yml +225 -0
  83. data/config/locales/en-US-pirate.yml +222 -0
  84. data/config/locales/en-US.yml +225 -0
  85. data/config/locales/en-YODA.yml +221 -0
  86. data/config/locales/en.yml +223 -0
  87. data/config/locales/es.yml +226 -0
  88. data/config/locales/fa.yml +223 -0
  89. data/config/locales/fr-CA.yml +227 -0
  90. data/config/locales/fr.yml +227 -0
  91. data/config/locales/he.yml +218 -0
  92. data/config/locales/hi.yml +223 -0
  93. data/config/locales/it.yml +225 -0
  94. data/config/locales/ja-JP.yml +215 -0
  95. data/config/locales/pt.yml +225 -0
  96. data/config/locales/ru.yml +228 -0
  97. data/config/locales/ur.yml +225 -0
  98. data/config/locales/zh-CN.yml +214 -0
  99. data/config/locales/zh-TW.yml +214 -0
  100. data/config/routes.rb +36 -0
  101. data/lib/ext/exception.rb +20 -0
  102. data/lib/generators/smriti/install/install_generator.rb +86 -0
  103. data/lib/generators/smriti/install/templates/create_mat_view_definitions.rb +29 -0
  104. data/lib/generators/smriti/install/templates/create_mat_view_runs.rb +32 -0
  105. data/lib/generators/smriti/install/templates/smriti_initializer.rb +23 -0
  106. data/lib/smriti/admin/auth_bridge.rb +93 -0
  107. data/lib/smriti/admin/default_auth.rb +62 -0
  108. data/lib/smriti/configuration.rb +58 -0
  109. data/lib/smriti/engine.rb +82 -0
  110. data/lib/smriti/helpers/ui_test_ids.rb +49 -0
  111. data/lib/smriti/jobs/adapter.rb +81 -0
  112. data/lib/smriti/service_response.rb +75 -0
  113. data/lib/smriti/services/base_service.rb +471 -0
  114. data/lib/smriti/services/check_matview_exists.rb +76 -0
  115. data/lib/smriti/services/concurrent_refresh.rb +94 -0
  116. data/lib/smriti/services/create_view.rb +173 -0
  117. data/lib/smriti/services/delete_view.rb +111 -0
  118. data/lib/smriti/services/regular_refresh.rb +90 -0
  119. data/lib/smriti/services/swap_refresh.rb +181 -0
  120. data/lib/smriti/version.rb +21 -0
  121. data/lib/smriti.rb +64 -0
  122. data/lib/tasks/helpers.rb +185 -0
  123. data/lib/tasks/smriti_tasks.rake +151 -0
  124. metadata +206 -0
@@ -0,0 +1,10 @@
1
+ <div class="mv-toolbar start">
2
+ <%= mv_drawer_link(new_admin_mat_view_definition_path(frame_id: 'mv-drawer'), t("smriti.mat_view_definition.new_definition"),
3
+ variant: :primary,
4
+ testid: 'NEW_DEFINITION_LINK',
5
+ tooltip: t("smriti.mat_view_definition.new_definition_tooltip")) do %>
6
+ <%= mv_icon(:plus_circle) %>
7
+ <%= t("smriti.mat_view_definition.new_definition") %>
8
+ <% end %>
9
+ </div>
10
+ <%= render "smriti/admin/ui/datatable", dt_config: @dt_config, row_values: @data %>
@@ -0,0 +1,40 @@
1
+ <% humanize_attr = ->(attr) { Smriti::MatViewDefinition.human_attribute_name(attr) } %>
2
+ <% humanize_enum_attr = ->(col, val) { Smriti::MatViewDefinition.human_enum_name(col, val) } %>
3
+
4
+ <input type="hidden" id="mv-drawer-title-text" value="<%= t("smriti.view_var", name: @definition.name) %>"/>
5
+ <input type="hidden" id="mv-drawer-open-url-identifier" value="definitions_view_<%= @definition.id %>"/>
6
+ <%= render "smriti/admin/ui/card" do %>
7
+ <div class="mv-field">
8
+ <div class="mv-label"><%= humanize_attr.call(:refresh_strategy) %></div>
9
+ <div><%= humanize_enum_attr.call(:refresh_strategy, @definition.refresh_strategy) %></div>
10
+ </div>
11
+ <div class="mv-field">
12
+ <div class="mv-label"><%= humanize_attr.call(:schedule_cron) %></div>
13
+ <div><%= @definition.schedule_cron.presence || "-" %></div>
14
+ </div>
15
+ <div class="mv-field">
16
+ <div class="mv-label"><%= humanize_attr.call(:unique_index_columns) %></div>
17
+ <div><%= Array(@definition.unique_index_columns).join(", ").presence || "-" %></div>
18
+ </div>
19
+ <div class="mv-field">
20
+ <div class="mv-label"><%= humanize_attr.call(:dependencies) %></div>
21
+ <div><%= Array(@definition.dependencies).join(", ").presence || "-" %></div>
22
+ </div>
23
+ <div class="mv-field">
24
+ <div class="mv-label"><%= humanize_attr.call(:last_run_at) %></div>
25
+ <div><%= @definition.last_run ? l_with_digits(@definition.last_run.started_at.in_time_zone, format: :datetime12hour) : "-" %></div>
26
+ </div>
27
+ <div class="mv-field">
28
+ <%= render "smriti/admin/ui/details",
29
+ tooltip_text: t("smriti.mat_view_definition.sql_summary_tooltip"),
30
+ title: content_tag(:div, humanize_attr.call(:sql), class: "mv-label") do %>
31
+ <pre><%= @definition.sql -%></pre>
32
+ <% end %>
33
+ </div>
34
+
35
+ <% content_for :footer do %>
36
+ <div class="mv-header-row even">
37
+ <%= render "definition_actions", defn: @definition, mv_exists: @mv_exists, frame_id: "mv-drawer" %>
38
+ </div>
39
+ <% end %>
40
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <tr class="mv-tr">
2
+ <td class="mv-td" colspan="<%= dt_config[:columns].size %>">
3
+ <div class="mv-cell">
4
+ <% if params[:dtfilter] || params[:dtsearch] %>
5
+ <%= t("smriti.mat_view_run.no_runs_if_filtered") %>
6
+ <% else %>
7
+ <%= t("smriti.mat_view_run.no_runs") %>
8
+ <% end %>
9
+ </div>
10
+ </td>
11
+ </tr>
@@ -0,0 +1,41 @@
1
+ <% dt_humanize_ref = dt_config[:dt_humanize_ref]
2
+ dt_humanize_ref_klass = dt_humanize_ref.constantize
3
+ humanize_attr = ->(attr) { dt_humanize_ref_klass.human_attribute_name(attr) }
4
+ humanize_enum_attr = ->(col, val) { dt_humanize_ref_klass.human_enum_name(col, val) }
5
+ mv_exists_map ||= {} %>
6
+
7
+ <tr class="mv-tr" data-testid="RUN_ROW_<%= row_value.id %>">
8
+ <td class="mv-td"><div class="mv-cell"><%= humanize_enum_attr.call(:operation, row_value.operation) %></div></td>
9
+ <td class="mv-td">
10
+ <div class="mv-cell">
11
+ <%= mv_drawer_link(admin_mat_view_definition_path(row_value.mat_view_definition, frame_id: "mv-drawer"), t("smriti.view_var", name: row_value.mat_view_definition.name),
12
+ variant: :ghost,
13
+ underline: true,
14
+ testid: 'VIEW_LINK',
15
+ testid_identifier: "defn-#{row_value.mat_view_definition.id}",
16
+ tooltip: t("smriti.mat_view_definition.view_tooltip")) do %>
17
+ <%= row_value.mat_view_definition.name %>
18
+ <% end %>
19
+ </div>
20
+ </td>
21
+ <td class="mv-td"><div class="mv-cell"><%= l_with_digits(row_value.started_at.in_time_zone, format: :datetime12hour) %></div></td>
22
+ <td class="mv-td">
23
+ <div class="mv-cell">
24
+ <%= mv_badge(row_value.status, humanize_enum_attr.call(:status, row_value.status)) %>
25
+ </div>
26
+ </td>
27
+ <td class="mv-td"><div class="mv-cell"><%= row_value.duration_ms ? localized_digits(t("smriti.x_miliseconds", count: row_value.duration_ms)) : "-" %></div></td>
28
+ <td class="mv-td"><div class="mv-cell"><%= localized_digits(row_value.row_count_before || "-") %>/<%= localized_digits(row_value.row_count_after || "-") %></div></td>
29
+ <td class="mv-td">
30
+ <div class="mv-cell">
31
+ <%= mv_drawer_link(admin_mat_view_run_path(id: row_value.id, frame_id: 'mv-drawer'), t('smriti.mat_view_run.view_details'),
32
+ variant: :ghost,
33
+ underline: true,
34
+ testid: 'VIEW_LINK',
35
+ testid_identifier: "run-#{row_value.id}",
36
+ tooltip: t("smriti.mat_view_run.view_tooltip")) do %>
37
+ <%= t("smriti.mat_view_run.view_details") %>
38
+ <% end %>
39
+ </div>
40
+ </td>
41
+ </tr>
@@ -0,0 +1 @@
1
+ <%= render "smriti/admin/ui/datatable", dt_config: @dt_config, row_values: @data %>
@@ -0,0 +1,64 @@
1
+ <% humanize_attr = ->(attr) { Smriti::MatViewRun.human_attribute_name(attr) } %>
2
+ <% humanize_enum_attr = ->(col, val) { Smriti::MatViewRun.human_enum_name(col, val) } %>
3
+ <input type="hidden" id="mv-drawer-open-url-identifier" value="runs_view_<%= @run.id %>"/>
4
+ <div class="mv-card">
5
+ <div class="mv-card-b">
6
+ <div class="mv-field">
7
+ <div class="mv-label"><%= t("smriti.definition") %></div>
8
+ <div>
9
+ <%= mv_drawer_link(admin_mat_view_definition_path(@run.mat_view_definition, frame_id: "mv-drawer"), t("smriti.view_var", name: @run.mat_view_definition.name),
10
+ variant: :ghost,
11
+ underline: true,
12
+ tooltip: t("smriti.mat_view_definition.view_tooltip")) do %>
13
+ <%= @run.mat_view_definition.name %>
14
+ <% end %>
15
+ </div>
16
+ </div>
17
+ <div class="mv-field">
18
+ <div class="mv-label"><%= humanize_attr.call(:operation) %></div>
19
+ <div><%= humanize_enum_attr.call(:operation, @run.operation) %></div>
20
+ </div>
21
+ <div class="mv-field">
22
+ <div class="mv-label"><%= humanize_attr.call(:started_at) %></div>
23
+ <div><%= l_with_digits(@run.started_at.in_time_zone, format: :datetime12hour) %></div>
24
+ </div>
25
+ <div class="mv-field">
26
+ <div class="mv-label"><%= humanize_attr.call(:finished_at) %></div>
27
+ <div><%= l_with_digits(@run.finished_at&.in_time_zone, format: :datetime12hour) || "-" %></div>
28
+ </div>
29
+ <div class="mv-field">
30
+ <div class="mv-label"><%= humanize_attr.call(:status) %></div>
31
+ <div><%= mv_badge(@run.status, humanize_enum_attr.call(:status, @run.status)) %></div>
32
+ </div>
33
+ <div class="mv-field">
34
+ <div class="mv-label"><%= humanize_attr.call(:duration_ms) %></div>
35
+ <div><%= @run.duration_ms ? localized_digits(t("smriti.x_miliseconds", count: @run.duration_ms)) : "-" %></div>
36
+ </div>
37
+ <div class="mv-field">
38
+ <div class="mv-label"><%= humanize_attr.call(:row_count_before) %></div>
39
+ <div><%= localized_digits(@run.row_count_before || "-") %></div>
40
+ </div>
41
+ <div class="mv-field">
42
+ <div class="mv-label"><%= humanize_attr.call(:row_count_after) %></div>
43
+ <div><%= localized_digits(@run.row_count_after || "-") %></div>
44
+ </div>
45
+
46
+ <div class="mv-field">
47
+ <%= render "smriti/admin/ui/details",
48
+ tooltip_text: t("smriti.mat_view_run.meta_tooltip"),
49
+ title: content_tag(:div, humanize_attr.call(:meta), class: "mv-label") do %>
50
+ <pre><%= JSON.pretty_generate(@run.meta || {}).strip -%></pre>
51
+ <% end %>
52
+ </div>
53
+
54
+ <% if @run.try(:error).present? %>
55
+ <div class="mv-field">
56
+ <%= render "smriti/admin/ui/details",
57
+ tooltip_text: t("smriti.mat_view_run.error_tooltip"),
58
+ title: content_tag(:div, humanize_attr.call(:error), class: "mv-label") do %>
59
+ <pre><%= JSON.pretty_generate(@run.error || {}).strip -%></pre>
60
+ <% end %>
61
+ </div>
62
+ <% end %>
63
+ </div>
64
+ </div>
@@ -0,0 +1,49 @@
1
+ <input type="hidden" id="mv-drawer-open-url-identifier" value="preferences_edit" %>
2
+ <div class="mv-card">
3
+ <div class="mv-card-b">
4
+ <%= form_with url: admin_preferences_path(frame_id: 'mv-drawer'), method: :put, data: { turbo_frame: :"mv-drawer" } do %>
5
+ <!-- Theme -->
6
+ <div class="mv-field">
7
+ <div class="mv-label"><%= t("smriti.settings.theme") %></div>
8
+ <p class="mv-label mv-label--sublabel">
9
+ <%= t("smriti.settings.theme_hint") %>
10
+ </p>
11
+ <div class="mv-actions" style="justify-content:flex-start">
12
+ <% [["auto", t("smriti.settings.auto")], ["light", t("smriti.settings.light")], ["dark", t("smriti.settings.dark")]].each do |value, label| %>
13
+ <label for="lang-<%= label %>" class="mv-btn mv-btn--secondary mv-btn--sm" style="gap:.4rem;">
14
+ <input id="lang-<%= label %>" type="radio" name="theme" value="<%= value %>" <%= "checked" if @theme == value %> style="margin:0"/>
15
+ <span><%= label %></span>
16
+ </label>
17
+ <% end %>
18
+ </div>
19
+ </div>
20
+ <br/>
21
+ <!-- Language -->
22
+ <div class="mv-field">
23
+ <label for="locale" class="mv-label"><%= t("smriti.settings.language") %></label>
24
+ <p class="mv-label mv-label--sublabel">
25
+ <%= t("smriti.settings.language_hint") %>
26
+ </p>
27
+ <select name="locale" id="locale" class="mv-select">
28
+ <% @locales.each do |loc| %>
29
+ <% label = Smriti::Engine.locale_code_mapping[loc.to_sym] %>
30
+ <option value="<%= loc %>" <%= "selected" if @locale == loc %>>
31
+ <%= label %>
32
+ </option>
33
+ <% end %>
34
+ </select>
35
+ </div>
36
+ <div class="mv-actions">
37
+ <%= mv_submit_button class: "mv-btn mv-btn--primary mv-btn--md",
38
+ testid: 'PREFERENCES_SAVE_BUTTON' do %>
39
+ <%= t("smriti.save_changes") %>
40
+ <% end %>
41
+
42
+ <%= mv_cancel_button variant: :ghost, data: { action: "click->drawer#close" },
43
+ testid: 'PREFERENCES_CANCEL_BUTTON' do %>
44
+ <%= t("smriti.cancel") %>
45
+ <% end %>
46
+ </div>
47
+ <% end %>
48
+ </div>
49
+ </div>
@@ -0,0 +1,15 @@
1
+ <% body = capture { yield } %>
2
+ <div class="mv-card">
3
+ <% if content_for?(:title) %>
4
+ <div class="mv-card-h">
5
+ <%= yield :title %>
6
+ </div>
7
+ <% end %>
8
+
9
+ <div class="mv-card-b"><%= body %></div>
10
+ <% if content_for?(:footer) %>
11
+ <div class="mv-card-f">
12
+ <%= yield :footer %>
13
+ </div>
14
+ <% end %>
15
+ </div>
@@ -0,0 +1,34 @@
1
+ <% dt_config = @dt_config
2
+ dt_humanize_ref = dt_config[:dt_humanize_ref]
3
+ dt_cols = dt_config[:columns]
4
+ dt_humanize_ref_klass = dt_humanize_ref.constantize
5
+ humanize_attr = ->(attr) { dt_humanize_ref_klass.human_attribute_name(attr) }
6
+ humanize_enum_attr = ->(col, val) { dt_humanize_ref_klass.human_enum_name(col, val) } %>
7
+
8
+ <div
9
+ data-controller="datatable"
10
+ data-datatable-index-url-value="<%= dt_config[:index_url] %>"
11
+ data-datatable-perpage-default="<%= dt_config[:pagination][:per_page_default]%>"
12
+ >
13
+ <table class="mv-table with-filters" id="<%= dt_config[:id] %>">
14
+ <%= render "smriti/admin/ui/datatable_thead" %>
15
+ <tbody id="datatable-body-<%= dt_config[:id] %>">
16
+ <tr class="mv-tr">
17
+ <td class="mv-td" colspan="<%= dt_cols.size %>">
18
+ <div class="mv-cell">
19
+ <%= t("smriti.loading") %>
20
+ </div>
21
+ </td>
22
+ </tr>
23
+ </tbody>
24
+ <tfoot>
25
+ <tr id="datatable-tfoot-<%= dt_config[:id] %>" class="mv-tr">
26
+ <td class="mv-td" colspan="<%= dt_cols.size %>">
27
+ <div class="dt-pagination-section" data-datatable-target="pagination" data-testid="DT_PAGINATION" data-testid-identifier="<%= dt_config[:id] %>-pagination">
28
+ <%= t("smriti.loading") %>
29
+ </div>
30
+ </td>
31
+ </tr>
32
+ </tfoot>
33
+ </table>
34
+ </div>
@@ -0,0 +1,45 @@
1
+ <% dt_config = @dt_config
2
+ dt_humanize_ref = dt_config[:dt_humanize_ref]
3
+ dt_cols = dt_config[:columns]
4
+ dtfilter_map = params[:dtfilter].present? ? params[:dtfilter].split(",").map { |f| f.split(":") }.to_h : {}
5
+ dt_humanize_ref_klass = dt_humanize_ref.constantize
6
+ humanize_attr = ->(attr) { dt_humanize_ref_klass.human_attribute_name(attr) } %>
7
+ <div class="mv-input-group" id="datatable-filters-<%= dt_config[:id] %>">
8
+ <div class="mv-field-inline">
9
+ <%= t("smriti.filter_by") %>
10
+ </div>
11
+ <% dt_cols.keys.each do |key| %>
12
+ <% next unless dt_cols[key][:filter].present? %>
13
+
14
+ <% value = dt_cols[key]
15
+ th_label =
16
+ if value[:label_type] == "i18n"
17
+ t("smriti.#{value[:label_ref]}")
18
+ elsif value[:label_type] == "humanize_attr"
19
+ humanize_attr.call(value[:label_ref])
20
+ else
21
+ value[:label_ref]
22
+ end %>
23
+
24
+ <% selected_value = dtfilter_map[key.to_s] || "no_filter"
25
+ options =
26
+ [[t("smriti.no_filter"), "no_filter"]] +
27
+ dt_humanize_ref_klass.send("filter_options_for_#{key.to_s}").map { |v| v[1].present? ? v : [t("smriti.no_value"), "no_value"] } %>
28
+ <div class="mv-field-inline">
29
+ <%= label_tag key, th_label, class: "mv-label" %>
30
+ <%= select_tag key,
31
+ options_for_select(options, selected_value),
32
+ class: "mv-select",
33
+ autocomplete: "off",
34
+ spellcheck: "false",
35
+ autocapitalize: "none",
36
+ autocorrect: "off",
37
+ data: {
38
+ action: "change->datatable#onFilterChange",
39
+ datatable_target: "filterField",
40
+ testid: "#{dt_config[:id]}-#{Smriti::Helpers::UiTestIds::DT_FILTER_SELECT}-#{key}",
41
+ key: key,
42
+ } %>
43
+ </div>
44
+ <% end %>
45
+ </div>
@@ -0,0 +1,11 @@
1
+ <% dt_config = @dt_config
2
+ row_values = Array(@data) %>
3
+ <tbody id="datatable-body-<%= dt_config[:id] %>">
4
+ <% if row_values.size.positive? %>
5
+ <% row_values.each do |row| %>
6
+ <%= render dt_config[:row_partial_name], dt_config: dt_config, row_value: row, **row_meta %>
7
+ <% end %>
8
+ <% else %>
9
+ <%= render dt_config[:empty_row_partial_name], dt_config: dt_config, **row_meta %>
10
+ <% end %>
11
+ </tbody>
@@ -0,0 +1,70 @@
1
+ <% dt_config = @dt_config %>
2
+ <% dt_cols = dt_config[:columns] %>
3
+ <tr id="datatable-tfoot-<%= dt_config[:id] %>" class="mv-tr">
4
+ <td class="mv-td" colspan="<%= dt_cols.size %>">
5
+ <div class="dt-pagination-section">
6
+ <%= label_tag :per_page, t("smriti.per_page"), class: "mv-label mr-2" %>
7
+ <%= select_tag :per_page,
8
+ options_for_select(dt_config[:pagination][:per_page_options].map { |opt| [opt, opt] }, @dt_per_page),
9
+ data: {
10
+ action: "change->datatable#changePerPage",
11
+ "datatable-target": "perPageSelect",
12
+ testid: "dt_pagination_btn-#{dt_config[:id]}-per-page",
13
+ },
14
+ class: "mv-input mv-input-sm",
15
+ aria: {
16
+ label: t("smriti.per_page"),
17
+ } %>
18
+
19
+ <%= mv_button data: { action: "click->datatable#goToPage", page: 1,
20
+ 'datatable-target': 'pageBtn' },
21
+ testid: "DT_PAGINATION_BTN",
22
+ testid_identifier: "#{dt_config[:id]}-page-first",
23
+ disabled: @dt_page == 1 do %>
24
+ <%= mv_icon(:double_arrow_left) %>
25
+ <% end %>
26
+ <%= mv_button variant: :ghost,
27
+ data: { action: "click->datatable#goToPage",
28
+ page: @dt_page - 1,
29
+ 'datatable-target': 'pageBtn' },
30
+ testid: "DT_PAGINATION_BTN",
31
+ testid_identifier: "#{dt_config[:id]}-page-previous",
32
+ disabled: @dt_page == 1 do %>
33
+ <%= mv_icon(:arrow_left) %>
34
+ <% end %>
35
+ <% pages = pagination_window(current_page: @dt_page, total_pages: @dt_total_pages) %>
36
+ <% pages.each_with_index do |page, index| %>
37
+ <% if page == :gap %>
38
+ <span class="mv-text-muted" data-testid="dt_pagination_btn_gap_<%= index %>">...</span>
39
+ <% else %>
40
+ <%= mv_button variant: (page == @dt_page ? :primary : :ghost),
41
+ data: { action: "click->datatable#goToPage",
42
+ page: page,
43
+ 'datatable-target': 'pageBtn' },
44
+ testid: "DT_PAGINATION_BTN",
45
+ testid_identifier: "#{dt_config[:id]}-page-#{page}" do %>
46
+ <%= localized_digits(page) %>
47
+ <% end %>
48
+ <% end %>
49
+ <% end %>
50
+ <%= mv_button variant: :ghost,
51
+ data: { action: "click->datatable#goToPage",
52
+ page: @dt_page + 1,
53
+ 'datatable-target': 'pageBtn' },
54
+ testid: "DT_PAGINATION_BTN",
55
+ testid_identifier: "#{dt_config[:id]}-page-next",
56
+ disabled: @dt_page == pages.last do %>
57
+ <%= mv_icon(:arrow_right) %>
58
+ <% end %>
59
+ <%= mv_button data: { action: "click->datatable#goToPage",
60
+ page: pages.last,
61
+ 'datatable-target': 'pageBtn' },
62
+
63
+ testid: "DT_PAGINATION_BTN",
64
+ testid_identifier: "#{dt_config[:id]}-page-last",
65
+ disabled: @dt_page == pages.last do %>
66
+ <%= mv_icon(:double_arrow_right) %>
67
+ <% end %>
68
+ </div>
69
+ </td>
70
+ </tr>
@@ -0,0 +1,105 @@
1
+ <% dt_config = @dt_config
2
+ dt_humanize_ref = dt_config[:dt_humanize_ref]
3
+ dt_cols = dt_config[:columns]
4
+ dt_humanize_ref_klass = dt_humanize_ref.constantize
5
+ search_enabled = dt_config[:search_enabled]
6
+ filter_enabled = dt_config[:filter_enabled]
7
+ humanize_attr = ->(attr) { dt_humanize_ref_klass.human_attribute_name(attr) }
8
+ humanize_enum_attr = ->(col, val) { dt_humanize_ref_klass.human_enum_name(col, val) } %>
9
+ <colgroup>
10
+ <% dt_cols.keys.each do |key| %>
11
+ <col class="col-<%= key.to_s.dasherize %>">
12
+ <% end %>
13
+ </colgroup>
14
+ <thead>
15
+ <% if search_enabled %>
16
+ <tr class="mv-tr">
17
+ <th class="mv-th" colspan="<%= dt_cols.size %>">
18
+ <div class="dt-filter-section">
19
+ <div class="mv-input-group">
20
+ <input
21
+ id="<%= dt_config[:id] %>-search-input"
22
+ name="dtsearch"
23
+ type="text"
24
+ class="mv-input mv-input--full-width"
25
+ placeholder="<%= t('smriti.datatable.search_placeholder') %>"
26
+ data-datatable-target="searchInput"
27
+ data-action="input->datatable#onSearchInput"
28
+ value="<%= params[:dtsearch].presence || '' %>"
29
+ autocomplete="off"
30
+ spellcheck="false"
31
+ autocapitalize="none"
32
+ autocorrect="off"
33
+ aria-label="<%= t('smriti.datatable.search_aria_label') %>"
34
+ data-testid='<%= "#{Smriti::Helpers::UiTestIds::DT_SEARCH_INPUT}-#{dt_config[:id]}-search" %>'
35
+ />
36
+ <%= mv_button variant: :tertiary, class: 'btn-clear mv-btn--no-padding', padding: false, data: { action: "click->datatable#clearSearchInput" },
37
+ aria: { label: t('smriti.datatable.clear_search_aria_label') },
38
+ testid: 'DT_CLEAR_SEARCH_BTN',
39
+ testid_identifier: "#{dt_config[:id]}-clear-search" do %>
40
+ <%= mv_icon(:x_circle) %>
41
+ <% end %>
42
+ </div>
43
+ </div>
44
+ </th>
45
+ </tr>
46
+
47
+ <% end %>
48
+ <% if filter_enabled %>
49
+ <tr class="mv-tr">
50
+ <th class="mv-th" colspan="<%= dt_cols.size %>">
51
+ <div class="dt-filter-section">
52
+ <div class="mv-input-group" id="datatable-filters-<%= dt_config[:id] %>">
53
+ <%= t("smriti.loading") %>
54
+ </div>
55
+ </div>
56
+ </th>
57
+ </tr>
58
+ <% end %>
59
+ <tr class="mv-tr">
60
+ <% dt_cols.keys.each do |key|
61
+ value = dt_cols[key]
62
+ sort = value[:sort]
63
+ sort_enabled = sort.present?
64
+ th_label = if value[:label_type] == 'i18n'
65
+ t("smriti.#{value[:label_ref]}")
66
+ elsif value[:label_type] == 'humanize_attr'
67
+ humanize_attr.call(value[:label_ref])
68
+ else
69
+ value[:label_ref]
70
+ end
71
+ data = {}
72
+ if sort_enabled
73
+ data = {
74
+ 'datatable-target': 'th',
75
+ key: key,
76
+ testid: 'TOGGLE_SORT_BUTTON',
77
+ testid_identifier: "#{dt_config[:id]}-#{key}",
78
+ }
79
+ end %>
80
+ <th class="mv-th" id="<%= dt_config[:id] %>-<%= key %>" <%= data.map { |k, v| "data-#{k.to_s.dasherize}=\"#{v}\"" }.join(' ').html_safe %>>
81
+ <div class="mv-cell" <%= value[:th_style].nil? ? '' : "style=\"#{value[:th_style]}\"".html_safe %>>
82
+ <% if sort_enabled %>
83
+ <span>
84
+ <%= th_label %>
85
+ </span>
86
+ <%= mv_button variant: :ghost, data: { action: "click->datatable#toggleSort" },
87
+ class: 'mv-btn--no-padding has-annotation',
88
+ padding: false,
89
+ testid: 'TOGGLE_SORT_BUTTON',
90
+ testid_identifier: "#{dt_config[:id]}-#{key}" do %>
91
+ <%= mv_icon(:sort_neutral, size: 24, class_name: "muted sort-neutral") %>
92
+ <%= mv_icon(:sort_asc, size: 24, class_name: "active hidden sort-asc") %>
93
+ <%= mv_icon(:sort_desc, size: 24, class_name: "active hidden sort-desc") %>
94
+ <span class="mv-annotation hidden"></span>
95
+ <% end %>
96
+ <% else %>
97
+ <span>
98
+ <%= th_label %>
99
+ </span>
100
+ <% end %>
101
+ </div>
102
+ </th>
103
+ <% end %>
104
+ </tr>
105
+ </thead>
@@ -0,0 +1,10 @@
1
+ <% body = capture { yield } %>
2
+ <details data-controller="details" data-details-duration-value="200" class="mv-details"
3
+ open>
4
+ <summary data-action="click->details#toggle" data-controller="tooltip" data-tooltip-text-value="<%= tooltip_text %>">
5
+ <%= mv_icon(:arrow_right, class_name: "mv-chevron") %>
6
+ <%= title %>
7
+ </summary>
8
+
9
+ <div data-details-target="content" class="mv-details__content"><%= body -%></div>
10
+ </details>
@@ -0,0 +1,6 @@
1
+ <% if notice %>
2
+ <div class="mv-flash mv-flash--ok" data-controller="flash"><%= notice %></div>
3
+ <% end %>
4
+ <% if alert %>
5
+ <div class="mv-flash mv-flash--err" data-controller="flash"><%= alert %></div>
6
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <table class="mv-table">
2
+ <% if local_assigns[:head].present? %>
3
+ <thead>
4
+ <tr class="mv-tr"><%= head %></tr>
5
+ </thead>
6
+ <% end %>
7
+ <tbody><%= yield %></tbody>
8
+ </table>
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ enable_integrity!
4
+ pin 'smriti/application'
5
+ pin '@hotwired/turbo-rails', to: 'turbo.min.js'
6
+ pin '@hotwired/stimulus', to: 'stimulus.min.js'
7
+ pin '@hotwired/stimulus-loading', to: 'stimulus-loading.js'
8
+
9
+ pin_all_from Smriti::Engine.root.join('app/javascript/smriti/controllers'), under: 'smriti/controllers'